検索/ほかのプロジェクトが継承するスマートコントラクトのライブラリを、壊さずに直す

ほかのプロジェクトが継承するスマートコントラクトのライブラリを、壊さずに直す

ほかのプロジェクトが継承して使うスマートコントラクトのライブラリを、AIに直させる時に使う。その動きを持つファイルを直し、linterとプロジェクトのツールに任せ、今ある関数・イベント・ストレージレイアウトを変えず、暗号やアップグレードなどAIが間違えやすい所は自分で読み直して確かめる。

このAIルールを使うには
## ほかのプロジェクトが継承する前提で作り、変更は先にissueで合意する - このリポジトリはスマートコントラクトライブラリであり、デプロイ済みのアプリケーションではなく、下流プロジェクトによって継承・拡張されることを前提とした再利用可能な抽象コントラクトとライブラリ群である。ここでの規約の多くは、このコードが他者の基盤となることを理由に存在している。 - 継承者がこれに依存するため、拡張を前提に設計すること。拡張が想定される箇所では関数はオーバーライド可能にし、引数の型は継承者を過度に制約せず、公開APIは特定の呼び出しコンテキストを子コントラクトに強制しないこと。 - まずリポジトリの正式な人間向けコントリビューターガイドを読むこと。それが正本である。AIアシスタント向けガイドは、支援作業に固有の内容や他所に記載されていない規約のみを扱う。両者が食い違う場合は人間向け仕様が優先される。 - 些細でない変更は、プルリクエストを開く前に必ずissueで議論すること。求められてもいないのに実装するのではなく、まず方向性について合意を得てから、その合意済みの範囲でPRを開くこと。 ## その動きを持つファイルを直し、自動生成ファイルは手で直さない - ライブラリのリポジトリにはいくつもの異なるサーフェスがある。ライブラリ本体のソース(公開API)、それを模したテスト専用モック、素のインターフェース定義、自動生成されたラッパー、形式検証仕様、ドキュメントのソース、過去の監査レポートなどである。ファイルを編集する前に、そのファイルがどのサーフェスに属するかを見極め、隣接するサーフェスではなく、実際にその挙動を所有するサーフェスを変更すること。1つのタスクが複数のサーフェスに同時にまたがることもあるため、確認せずに「単なるコードだ」「単なるドキュメントだ」と決めつけないこと。 - 自動生成されたファイルやツールが管理するファイルを手作業で編集しないこと。テンプレートから機械的に生成されたファイルにはその旨を示すヘッダーが付いている。そうしたファイルはテンプレート側を編集して再生成すること。CIが生成差分チェックで鮮度を強制しており、手作業での編集は上書きされてしまう。自動生成されたラッパーは通常gitignoreされており、必要に応じて再ビルドされる。 - ツールが管理するメタデータはそのツールに任せること。ファイルごとの「最終更新バージョン」ヘッダー行は、リリースツールがバージョン管理履歴から自動的に維持している。これを追加・編集しないこと。あなたの責任範囲は、ライセンス識別子の行など、ツールが所有していない部分に限られる。 ## 名前と公開範囲の決まりはlinterに任せ、レビューで繰り返さない - 設定済みのlinterが、ライブラリ本体のソースに対する機械的な規約をすでに強制している(モックとテストは通常対象外)。状態変数はデフォルトでprivateとし、例外は`constant`と`immutable`のみとする。アンダースコア接頭辞は可視性に対応させ、privateおよびinternalの状態変数と関数には先頭にアンダースコアを付け、publicおよびexternalのメンバーには付けない。インターフェース名には`I`を接頭辞として付ける。コントラクトとイベントはCapWords形式を使う。パラメータとmodifierはmixedCase形式を使う。ライブラリはinternalまたはprivateの関数のみを公開する。 - lintルールがすでに検出している問題については、コードレビューでそれを説明するのにトークンを費やさないこと。修正して先に進めばよい。lintが強制しているルールをレビューコメントで繰り返すのは、シグナルではなくノイズである。 ## linterでは見つけられない、コンパイラのバージョンとimportの決まりを守る - コンパイラのpragma下限:最小バージョンを手動で選ばないこと。プロジェクトのpragma最小化ツールを実行すること。このツールは依存関係グラフをたどり、候補となる各コンパイラバージョンに対してコンパイルを行い、ファイルごとに動作する最小バージョンを書き戻す(インターフェースには`>=`接頭辞、実装とライブラリには`^`を使う)。下限を安易に引き上げると、一部の対象チェーンでサポートされないopcodeを取り込んでしまう可能性があるため、このツールの出力が安全な答えとなる。 - importは必ず名前付き(波括弧を使用)とし、ソースツリー内での相対パスで100%記述すること。ワイルドカードimportは禁止。 - 継承の順序はリポジトリ全体で一貫していなければならない。`is A, B, C`のようなリストを変更した場合は、pushする前に再コンパイルし、継承順序チェックを実行すること。 - Changelogへの記載:コントラクトの挙動を変更するすべての変更には記載が必要である。省略してよいのは、ドキュメントのみの編集、ユーザーに影響のない内部リファクタリング、あるいは純粋なリポジトリの雑務的な変更に限る。 ## 今ある関数とストレージレイアウトを変えず、依存関係を増やさない - リリース済みのコントラクトは下流で継承されており、別のアップグレード可能なバリアントにトランスパイルされることも多い。既存のpublicまたはexternal関数のシグネチャ、既存のイベントやエラーの形状、既存コントラクトのストレージレイアウトを変更しないこと。変更は加算的に行い、削除ではなく非推奨化とすること。破壊的変更はメジャーリリースに紐づくメンテナーの決定事項であり、通常の作業の中で持ち込むべきものでは決してない。 - 依存関係のサーフェスは最小限に保つこと。外部に手を伸ばす前に、ライブラリ既存のユーティリティヘルパーを再利用すること。メンテナーの承認なしにサードパーティ製パッケージや新しい外部importを追加しないこと。より重いサードパーティ依存を本当に必要とするコードは、コアライブラリではなく、別のコミュニティリポジトリや拡張用リポジトリに置くべきである。 ## 暗号やストレージスロットの、AIが間違えやすいコードを読み直して確かめる - これらの領域では、AIの提案は一見もっともらしくても誤っていることが多い。変更を受け入れる前に、周辺のコードを読み、その主張を自分で再導出すること。 - 暗号処理:ハッシュ化、ECDSA、typed-data(EIP-712)のドメインセパレータ、署名方式、ペアリングやBLS演算。 - 名前空間付き(ERC-7201)スロットなど、ストレージスロットの導出:正規の導出ヘルパーで再計算し、宣言済みの定数と照合すること。インラインコメント中の式は装飾ではなく、必ず守るべきものである。 - プロキシやトランスパイラで処理されるコントラクトのストレージレイアウト:フィールドの追加・削除・並べ替えは、すでにデプロイ済みのプロキシを壊す。CIでも実行されるが、ストレージレイアウトのdiffは自分でも必ず読むこと。 - `delegatecall`、インラインアセンブリ、メモリ安全性:memory-safeの注釈が実態と一致しているか、スタックとスクラッチ領域の使用量が有界であるかを検証すること。 - `unchecked`ブロック:すべてのコードパスについて、オーバーフローが起こり得ないという根拠を自分で再導出すること。インラインの正当化コメントは、あなたが守らなければならない契約である。 - アップグレードの権限管理:authorize-upgradeフックはベースでは意図的に空になっているため、すべてのオーバーライドで誰が呼び出せるかを制限しなければならない。 - プロキシ実装のコンストラクタ:UUPSまたはtransparent-proxyの実装対象で、(例えばdisable-initializers呼び出しなどにより)initializerが無効化されていることを確認すること。 ## 公開APIの変更やセキュリティの判断は、AIでなく人に任せる - 公開APIの追加・リネーム、あるいは機能をそもそもこのライブラリに含めるべきかどうかの判断:まずissueを開き、メンテナーの決定を得ること。 - セキュリティ評価および監査上の見解:コントラクトが安全であると断定するのではなく、人間の監査人の判断に委ねること。 - 明確な指示なしに新しいインターフェース標準を導入すること。 - 対応するコントラクトの変更を伴わないテストの再構成。 - セキュリティ報奨をでっち上げないこと。バグバウンティプログラム、報奨金額、対象資格について、主張・示唆・捏造をしないこと。実際に存在する場合に限り、公式に文書化されたプログラムのみを参照すること。