develop-secure-contracts について
複数のエコシステムで OpenZeppelin を統合するスマートコントラクト開発者向けのスキルです。既存のプロジェクトと依存関係を調べ、ライブラリのコンポーネントを優先して必要最小限の変更を適用します。
主な機能
- 01プロジェクトのコントラクトと依存関係を確認
- 02OpenZeppelin のライブラリコンポーネントを優先
- 03ソースとドキュメントから統合パターンを調査
- 04CLI で参考コードを生成して比較
- 05既存コントラクトに必要な変更を適用
活用シーン
- 01既存コントラクトにアクセス制御や一時停止を追加
- 02各エコシステムのライブラリと使い方を調査
- 03生成した参考コードから機能追加の変更点を特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ openzeppelin/ openzeppelin-skills --skill develop-secure-contractsインストール後、Claude Code で/develop-secure-contractsを入力して読み込まれたか確認します。
詳細
OpenZeppelin でセキュアなスマートコントラクトを開発する
基本的なワークフロー
回答する前にリクエストを理解する
概念に関する質問(「Ownable はどのように動作しますか?」など)には、コードを生成せずに説明してください。実装の依頼には、以下のワークフローに従ってください。
重要:必ず最初にプロジェクトを確認する
コードを生成したり変更を提案したりする前に、次の手順を実行してください。
- ユーザーのプロジェクトを検索し、既存のコントラクトを確認します(
Globで**/*.sol、**/*.cairo、**/*.rs、**/*.moveなどを検索します) - 関連するコントラクトファイルを読み、すでにあるものを把握します
- 置き換えではなく、既存のコードへの統合を基本とします。ユーザーが「一時停止機能を追加して」や「アップグレード可能にして」と言った場合、既存のコードを変更するという意味であり、新しいものを生成するという意味ではありません。明示的に依頼された場合(「新しく作成して」「これを置き換えて」など)のみ、置き換えてください。
ファイルを読み取れない場合は、試したパスと理由を明示して、読み取りに失敗したことを伝えてください。そのパスが正しいかどうかを確認してください。ファイルが存在しないかのように、一般的な回答へひそかに切り替えてはいけません。
基本ルール:カスタムコードよりライブラリのコンポーネントを優先する
ロジックを書く前に、必ず OpenZeppelin ライブラリに既存のコンポーネントがないか検索してください。
- 完全に一致するものがありますか? それを直接インポートして使用します。継承する、トレイトを実装する、または組み合わせて使用すれば完了です。
- 近いものがありますか? それをインポートして拡張します。ライブラリがオーバーライド可能としている関数(virtual、フック、設定可能なパラメーター)のみをオーバーライドしてください。
- 一致するものがありませんか? その場合に限り、カスタムロジックを書きます。まずライブラリのディレクトリ構成を確認してから判断してください。
ライブラリのソースコードをユーザーのコントラクトにコピーしたり、埋め込んだりしてはいけません。 常に依存関係からインポートし、プロジェクトでセキュリティアップデートを受け取れるようにしてください。ライブラリがすでに提供している機能を、決して独自に実装しないでください。
PausableまたはERC20Pausableがある場合、カスタムのpaused修飾子を書いてはいけませんOwnableがある場合、require(msg.sender == owner)を書いてはいけません- ライブラリの基底コントラクトがすでに処理している場合、ERC165 のロジックを実装してはいけません
方法
主要なワークフローは、ライブラリのソースコードからパターンを見つけることです。
- ユーザーのプロジェクトがすでにインポートしているものを調べます
- プロジェクトにインストールされたパッケージ内の依存関係のソースコードとドキュメントを読みます
- 依存関係が必要とする関数、修飾子、フック、ストレージを特定します
- その要件をユーザーのコントラクトに適用します
手順の詳細については、以下のパターンの発見と統合を参照してください。
CLI ジェネレーターをリファレンスとして使用する
パターンの発見には、npx @openzeppelin/contracts-cli を使ってリファレンス実装を生成します。まずベースラインをファイルに生成し、機能を有効にしたものを別のファイルに生成して、両者の差分を確認したうえで、変更をユーザーのコードに適用します。CLI の出力は、正しい統合方法を示す標準的な実装です。機能に必要なインポート、継承、ストレージ、オーバーライドを判断するための信頼できる情報源として使用してください。
詳しくは、生成・比較・適用のワークフローについてのCLI ジェネレーターを参照してください。
必要な機能に対応する CLI コマンドがない場合は、パターンの発見と統合に記載されている汎用的なパターン発見の方法に従ってください。CLI コマンドがないからといって、ライブラリがその機能に対応していないという意味ではありません。単にジェネレーターがないということです。
パターンの発見と統合
依存関係のソースコードを読み、OpenZeppelin コントラクトの統合パターンを見つけて適用するための手順ガイドです。 あらゆるエコシステムとライブラリのバージョンで使用できます。
前提条件: 必ず上記のライブラリ優先の判断手順に従ってください (カスタムコードよりライブラリのコンポーネントを優先し、ソースコードをコピーしたり埋め込んだりしないでください)。
ステップ 1:依存関係を特定してライブラリを検索する
- プロジェクト内でコントラクトファイルを検索します。
Globを使って**/*.sol、**/*.cairo、**/*.rs、**/*.move、または以下の対応表にある該当する拡張子を検索します。 - 既存のコントラクトにある import/use 文を読み、すでに使用されている OpenZeppelin コンポーネントを特定します。
- プロジェクトの依存関係ツリーから、インストール済みの依存関係を探します。
- Solidity:
node_modules/@openzeppelin/contracts/(Hardhat/npm)またはlib/openzeppelin-contracts/(Foundry/forge) - Cairo:
Scarb.tomlの依存関係から解決します。ソースは Scarb によってキャッシュされます。 - Stylus:
Cargo.tomlから解決します。ソースはtarget/または Cargo レジストリのキャッシュ (~/.cargo/registry/src/)にあります。 - Stellar:
Cargo.tomlから解決します。Stylus と同じ Cargo キャッシュの場所にあります。 - Sui Move:
Move.tomlから解決します。ビルド後、MVR ソースは~/.move/にキャッシュされ、 依存関係ごとにbuild/<project_package>/sources/dependencies/<move_package_name>/にも複製されます。
- Solidity:
- 依存関係のディレクトリ一覧を確認し、利用可能なコンポーネントを探します。インストール済みソースに対して
Globパターンを使用します(例:node_modules/@openzeppelin/contracts/**/*.sol)。 ライブラリの内容を知っていると思い込まず、必ずディレクトリ一覧で確認してください。 - 依存関係がローカルにインストールされていない場合は、正規リポジトリをクローンするか参照します (以下の対応表を参照)。
ステップ 2:依存関係のソースとドキュメントを読む
- ユーザーのリクエストに関連するコンポーネントのソースファイルを読みます。
- ソース内のドキュメントを確認します。Solidity の NatSpec コメント(
///、/** */)、Rust と Cairo の ドキュメントコメント(///)、およびコンポーネントのディレクトリ内にある README ファイルを探します。 - Critical Principle の判断ツリーを使って、統合方法を決定します。
- コンポーネントが要件を直接満たす場合 → そのまま import して使用します。
- カスタマイズが必要な場合 → ライブラリが提供する拡張ポイント(virtual 関数、フック関数、設定可能なコンストラクターパラメーター)を特定します。import して拡張します。
- 要件を満たすコンポーネントがない場合に限り → カスタムロジックを記述します。
- パブリック API を特定します。公開される関数/メソッド、発行されるイベント、定義されるエラーを確認します。
- 統合要件 を特定します。ここが重要なステップです。
- 統合する側が必ず実装しなければならない関数(抽象関数、トレイトメソッド、フック)
- 統合する側の関数に適用しなければならないモディファイア、デコレーター、ガード
- 渡さなければならないコンストラクターまたはイニシャライザーのパラメーター
- 宣言しなければならないストレージ変数または状態
- 必要な継承またはトレイト実装(必ず import を使い、コピーはしない)
- 正しい使用方法を示すサンプルコントラクトやテストを同じリポジトリ内で探します。
test/、tests/、examples/、またはmocks/ディレクトリを確認します。
ステップ 3:最小限の統合パターンを抽出する
ステップ 2 をもとに、必要最小限の変更をまとめます。
- 追加する Imports / use 文
- 追加する 継承 / トレイト実装(必ず依存関係から import する)
- 宣言する ストレージ
- コンストラクター / イニシャライザー の変更(新しいパラメーター、初期化呼び出し)
- 追加する 新しい関数(必須のオーバーライド、フック、パブリック API)
- 変更する 既存の関数(モディファイアの追加、フックの呼び出し、イベントの発行)
コントラクトがアップグレード可能な場合、上記のいずれもストレージの互換性に影響する可能性があります。適用前に、該当するアップグレードスキルを参照してください。
依存関係が必要とするもの以外は追加しないでください。これは「機能のないコントラクト」と「機能のあるコントラクト」の間に必要な最小限の差分です。
ステップ 4:ユーザーのコントラクトにパターンを適用する
- ユーザーが既存のコントラクトファイルを読み込みます。
Editツールを使って、ステップ 3 の変更を適用します。ファイル全体を置き換えず、既存のコードに統合してください。- 競合がないか確認します。アクセス制御システムの重複、関数オーバーライドの競合、互換性のない継承を確認し、完了前に解決してください。
- ユーザー自身に変更を依頼せず、直接適用してください。
リポジトリとドキュメントの参照表
| エコシステム | リポジトリ | ドキュメント | ファイル拡張子 | 依存関係の場所 |
|---|---|---|---|---|
| Solidity | openzeppelin-contracts | docs.openzeppelin.com/contracts | .sol | node_modules/@openzeppelin/contracts/ または lib/openzeppelin-contracts/ |
| Cairo | cairo-contracts | docs.openzeppelin.com/contracts-cairo | .cairo | Scarb キャッシュ(Scarb.toml から解決) |
| Stylus | rust-contracts-stylus | docs.openzeppelin.com/contracts-stylus | .rs | Cargo キャッシュ(~/.cargo/registry/src/) |
| Stellar | stellar-contracts (アーキテクチャ) | docs.openzeppelin.com/stellar-contracts | .rs | Cargo キャッシュ(~/.cargo/registry/src/) |
| Sui Move | contracts-sui (llms.txt · アーキテクチャ) | docs.openzeppelin.com/contracts-sui | .move | Move Registry キャッシュ(~/.move/、Move.toml から解決) |
ディレクトリ構成の規則
各リポジトリ内でコンポーネントを見つける場所:
| カテゴリ | Solidity | Cairo | Stylus | Stellar |
|---|---|---|---|---|
| トークン | contracts/token/{ERC20,ERC721,ERC1155}/ | packages/token/ | contracts/src/token/ | packages/tokens/ |
| アクセス制御 | contracts/access/ | packages/access/ | contracts/src/access/ | packages/access/ |
| ガバナンス | contracts/governance/ | packages/governance/ | — | packages/governance/ |
| プロキシ/アップグレード | contracts/proxy/ | packages/upgrades/ | contracts/src/proxy/ | packages/contract-utils/ |
| ユーティリティ/セキュリティ | contracts/utils/ | packages/utils/, packages/security/ | contracts/src/utils/ | packages/contract-utils/ |
| アカウント | contracts/account/ | packages/account/ | — | packages/accounts/ |
コンポーネントを探すときは、まずこれらのパスを確認してください。
Sui Move はこの固定グリッドには含まれず、@openzeppelin/contracts-cli ジェネレーターもありません。そのため、常に上記のパターン発見手法を使用してください。パッケージの examples/ を標準的な統合手順として活用し、ソースをコピーするのではなく MVR 経由でインポートしてください。それ以外(パッケージ構成、組み合わせ方とスタイルの規則、正確な API、ツールチェーン)は、ライブラリ独自のメタデータからすべて確認します。まずは llms.txt を参照してください。setup-sui-contracts スキルでは、セットアップ、依存関係、--build-env ビルド、品質ゲートの一連の手順を網羅しています。
バージョン固有の既知の考慮事項
以前の知識に基づいてオーバーライドポイントを推測せず、必ずインストール済みのソースを確認してください。以前のバージョンで virtual だった関数が現行バージョンではそうでなくなり、オーバーライドできない場合があります。正しいオーバーライドポイントは、ソースの NatSpec に記載されています(例:NOTE: This function is not virtual, {X} should be overridden instead)。
既知の例として、Solidity の ERC-20 転送フックは v4 と v5 の間で変更されています。オーバーライドを推奨する前に、インストール済みの ERC20.sol を読んで、どの関数が virtual か確認してください。
CLI ジェネレーター
@openzeppelin/contracts-cli パッケージは、コマンドラインから OpenZeppelin コントラクトの参照実装を生成します。コントラクトの種類に対応するコマンドがある場合は、generate-compare-apply ワークフローで参照ソースとして使用してください。
コマンドとオプションの確認
npx @openzeppelin/contracts-cli --help を実行して、利用可能なコマンドを一覧表示します。各コマンドはコントラクトの種類に対応しています(例:solidity-erc20、cairo-erc721、stellar-fungible)。npx @openzeppelin/contracts-cli <command> --help を実行すると、利用可能なオプションを確認できます。利用できるオプションについて以前の知識に頼らず、会話の開始時に --help を確認してください。CLI は更新されている場合があります。
Generate-Compare-Apply のショートカット
コントラクトの種類に対応する CLI コマンドがある場合は、生成した出力を一時ファイルにパイプして差分を比較し、生成されたコントラクトコードが会話のコンテキストに入らないようにします。
- ベースラインを生成 — 必須のオプションのみを指定し、すべての機能を無効にして、ファイルにパイプします。
npx @openzeppelin/contracts-cli solidity-erc20 --name MyToken --symbol MTK > /tmp/oz-baseline.sol - 機能を有効にして生成 — 機能を有効にして再度実行し、2つ目のファイルにパイプします。
npx @openzeppelin/contracts-cli solidity-erc20 --name MyToken --symbol MTK --pausable > /tmp/oz-variant.sol - 比較 — 2つのファイルを diff して、変更点(インポート、継承、状態、コンストラクター、関数、修飾子)を正確に特定します。
diff /tmp/oz-baseline.sol /tmp/oz-variant.sol - 適用 — ユーザーの既存のコントラクトを編集し、特定した変更を追加します。
相互に影響する機能(例:アクセス制御とアップグレード可能性)については、組み合わせたバリアントも生成します。
CLI コマンドがない場合、または機能に対応していない場合
CLI コマンドがないからといって、ライブラリにサポートがないとは限りません。そのコントラクトの種類に対応したジェネレーターがないというだけです。パターンの発見と統合にある一般的なパターンの発見方法を必ず利用してください。
同様に、CLI コマンドがあっても特定の機能のオプションがない場合は、そこで終わりにしないでください。その機能についてはパターンの発見に切り替え、インストール済みのライブラリソースを読んで関連コンポーネントを見つけ、統合に必要な要件を抽出して、ユーザーのコントラクトに適用します。