mcpField
S
solidity-coding

solidity-coding

github.com/0xlayerghost開発4134 回インストール3 週間前に更新
リポジトリ

solidity-coding について

Solidityコントラクトを開発する人向けのコーディング規約スキルで、初期化、安全性、イベント、プロジェクト構成を扱います。コントラクトの条件に応じた初期化方式とOpenZeppelinコンポーネントの選択を促します。

主な機能

  1. 01Solidityのバージョンと記述規則
  2. 02初期化方式と安全ルール
  3. 03イベント設計と命名規則
  4. 04コントラクト構成と設定管理
  5. 05OpenZeppelinコンポーネントの選択

活用シーン

  1. 01Solidityのコーディング規約の策定
  2. 02コントラクトの初期化方式の選択
  3. 03イベント、エラー、アクセス制御の設計
  4. 04Foundryプロジェクトファイルの整理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/0xlayerghost/solidity-agent-kit --skill solidity-coding

インストール後、Claude Code で/solidity-codingを入力して読み込まれたか確認します。

同梱ファイルreferences/complex-function-refactoring.mdreferences/shared-contract-behaviors.md

詳細

Solidity コーディング標準

言語ルール

  • 常にユーザーが使用している言語で応答してください。 ユーザーが中国語で質問した場合は中国語で、英語で質問した場合は英語で応答してください。

コーディング原則

  • プラグマ:pragma solidity ^0.8.19; を使用し、プロジェクト内のすべてのファイルで統一してください
  • 依存関係:OpenZeppelin Contracts 4.9.x を使用し、remappings.txt でインポートを管理してください
  • エラー処理:require の文字列よりカスタムエラーを優先してください。ガスを節約でき、より明確に表現できます
    • 定義:error InsufficientBalance(uint256 available, uint256 required);
    • 使用:if (balance < amount) revert InsufficientBalance(balance, amount);
  • ドキュメント:すべての public/external 関数に NatSpec(@notice、@param、@return)を記述してください
  • イベントのインデックス化:indexed は address 型のパラメーターにのみ追加してください。それ以外の型をインデックス化する場合はコメントを追加してください
  • 特殊キーワード:immutable/constant/unchecked/assembly には、使用理由を説明するインラインコメントを付けてください

初期化戦略

コントラクトで状態の初期化(外部アドレス、ビジネスパラメーター、ロールの割り当て)が必要な場合、単一の方式をデフォルトとして選ばないでください。次のワークフローに従ってください。

ワークフロー
  1. 評価する:コントラクトはアップグレード可能ですか? デプロイ時にすべての依存関係が判明していますか? コントラクト間のデプロイ順序に制約はありますか?
  2. 選択肢を提示する:以下の3つの初期化方式と、それぞれのトレードオフを開発者に示してください
  3. 開発者に選んでもらう:方式を決めつけないでください。プロジェクトやコントラクトによって必要な方式は異なります
  4. 実装する:選択した方式を、以下の安全ルールに従って適用してください
方式の比較
方式 A:コンストラクターのみ方式 B:イニシャライザーのみ方式 C:ハイブリッド
アプローチすべての初期化をコンストラクターパラメーターで行う空の状態でデプロイし、後から initialize() を呼び出すコンストラクターで判明済みのパラメーターを処理し、initialize() でデプロイ後の依存関係を処理する
アトミック性デプロイに失敗すると何も作成されず、危険な未初期化状態にならない「デプロイ済みだが未初期化」の期間が生じるリスクがある部分的にアトミック。コンストラクターの部分は安全だが、イニシャライザーの部分にはその期間が生じる
フロントランニングのリスクなし。コンストラクターはフロントランニングできない保護されていない場合、initialize() がフロントランニングされる可能性があるイニシャライザー部分にのみリスクがある
ガスデプロイと初期化を1回のトランザクションで行うデプロイのトランザクション+N回の初期化トランザクションデプロイのトランザクション+1回の初期化トランザクション
柔軟性低い。デプロイ時にすべてのパラメーターが判明している必要がある高い。他のコントラクトのデプロイ後に依存関係を設定できる中程度。デプロイ時とデプロイ後で分ける
アップグレード可能性プロキシパターンと互換性がないUUPS/Transparent Proxy で必須部分的。コンストラクターでプロキシに安全なパラメーターを設定し、残りはイニシャライザーで設定する
各方式を提案する場面
シナリオ推奨方式
アップグレード不可で、すべての依存関係がデプロイ時に判明しているA
アップグレード可能なコントラクト(UUPS/Transparent Proxy)B
アップグレード不可だが、一部の依存関係が後からデプロイされる(例:トークンのデプロイ後にLPペアを作成する)C
相互参照のある複数コントラクトのシステムC
シンプルなユーティリティ/ライブラリ形式のコントラクトA
デプロイ後にマルチシグ/DAO による設定が必要なコントラクトB または C
安全ルール(方式にかかわらず適用)
  • 一度限りのロック:一度しか呼び出してはならないセッターでは、require(currentValue == address(0)) または bool initialized フラグを使って再初期化を防止します
  • アクセス制御のない初期化関数は禁止:initialize() 関数には、アクセス制御(onlyOwner、initializer 修飾子、またはデプロイ者のみのチェック)を設ける必要があります
  • 不変値を優先:デプロイ時に値が確定している場合は、通常のストレージではなく immutable を使用します。読み取りのたびに発生するガスを節約できます
  • コンストラクター内で複雑な外部呼び出しをしない:コンストラクターから、状態変更を伴う操作(swap、addLiquidity)のために外部コントラクトを呼び出してはなりません。コントラクトの構築がまだ完了していないためです
  • デプロイスクリプトの責任:方式 C を使用する場合、デプロイスクリプトは同じトランザクションバッチ内、または直後に initialize() を呼び出す必要があります。必要な呼び出し順序を文書化してください

イベント設計パターン

理由を注記するイベント

イベントが条件分岐の結果を表す場合(同じアクションが異なる理由で発生する場合)、オンチェーンデータだけで内容がわかるように string reason パラメーターを追加します:

// GOOD — on-chain data is self-explanatory
event WithdrawalFailed(address indexed user, uint256 amount, string reason);
emit WithdrawalFailed(user, amount, "insufficient balance");
emit WithdrawalFailed(user, amount, "cooldown not expired");

event ProposalRejected(uint256 indexed proposalId, string reason);
emit ProposalRejected(id, "quorum not reached");
emit ProposalRejected(id, "voting period ended");

// BAD — must read contract source to understand why
event WithdrawalFailed(address indexed user, uint256 amount);

適用する場面:

  • 資金の行き先が分岐する場合:同じ資金が異なる条件に応じて異なるアドレスに送られる
  • 操作が拒否/縮小される場合:ユーザーのリクエストが完全には満たされず、その理由がある
  • 状態遷移:同じ状態変更が異なるトリガー条件によって起こる可能性がある
  • 管理者の操作:理由を記録する場合(例:"security incident"、"parameter tuning")

適用しない場面:

  • イベントのトリガー経路が1つだけの場合(分岐がない)→ reason は不要です
  • 高頻度イベント(各 swap/transfer)→ string は追加のガスを消費するため、代わりに uint8 reasonCode を使用します
Reason の実装方式の選択
場面方式ガスコスト
理由の種類が少なく固定されている(≤5種)string reason のリテラル低(コンパイラーが短い文字列を最適化)
理由の種類が多い、または動的であるuint8 reasonCode + フロントエンドのマッピングテーブル最低
動的なデータを含める必要があるstring reason の連結高め。使用は慎重に
// 方式 A:string literal(推荐,可读性最好)
emit WithdrawalFailed(user, amount, "cooldown not expired");

// 方式 B:uint8 code(高频场景省 gas)
event SwapRejected(address indexed user, uint256 amount, uint8 reasonCode);
// 0 = slippage, 1 = insufficient liquidity, 2 = paused
emit SwapRejected(user, amount, 0);
イベント設計の一般ルール
  • 状態を変更する external/public 関数は、それぞれ少なくとも1つのイベントを emit しなければなりません
  • イベントパラメーターには、フロントエンド/indexer が追加の RPC 呼び出しなしで完全なコンテキストを再構築できるだけの情報を含める必要があります
  • NatSpec の @param には、reason パラメーターで使用可能な値をすべて列挙する必要があります

命名規則

要素規則例
コントラクト/ライブラリPascalCaseMyToken、StakingPool
インターフェースI + PascalCaseIMyToken、IStakingPool
状態変数/関数lowerCamelCasetotalSupply、claimDividend
定数/不変変数UPPER_SNAKE_CASEMAX_SUPPLY、ROUTER_ADDRESS
イベントPascalCase(過去形)TokenTransferred、PoolCreated
カスタムエラーPascalCaseInsufficientBalance、Unauthorized
関数パラメーターセッターでは接頭辞 _ を使用function setFee(uint256 _fee)
  • 禁止事項:ピンイン名、単一文字の変数名(ループ内の i/j/k を除く)、過度な略語
  • 戻り値の命名:戻り値の変数名が状態変数名と競合する場合、末尾に _ を付けるのではなく、意味を表す接頭辞(is/can/has/current/total)を使用します。フロントエンド利用者向けの ABI をすっきり保てます

コード構成ルール

状況ルール
コントラクト間で共有する定数src/common/Const.sol に配置します
インターフェース定義実装とは分けて src/interfaces/I<Name>.sol に配置します
共有する状態を持つ振る舞いsrc/abstract/<Name>.sol に配置し、実装をコピーするのではなく継承します
単純なオンチェーン照会Foundry cast CLI(call/send)を使用します
複雑な複数ステップの操作Foundry スクリプト(*.s.sol)を使用します
インポート形式名前付きインポートを使用します:import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
共有管理機能

複数のコントラクトで同じ管理操作が必要な場合は、関数名、パラメーターの順序、イベント、エラー、NatSpec、実装を正規のものに統一してください。新しいバリエーションを追加する前に、リポジトリを検索してください。

状態を伴う動作やモディファイアには共有の抽象コントラクトを、外部の利用者に安定した ABI が必要な場合はインターフェースを、状態を伴わないロジックにはライブラリを使用してください。業務コントラクトは、共有実装をコピーせず、必ず継承してください。

プロジェクトで正規とされる、オーナーが制御する資産回収関数と継承ルールについては、references/shared-contract-behaviors.md を参照してください。プロジェクトのポリシーでは、意図的にオーナーを最上位の権限として扱い、コントラクトが保有するすべての ERC20 トークンとネイティブ通貨の回収を許可しています。

複雑な関数のリファクタリング

ある関数が複数の業務段階を調整する場合、検証と複数の状態変更または外部呼び出しが混在する場合、あるいはコンパイラーのスタック上限に近づいている場合は、変更する前に references/complex-function-refactoring.md を参照してください。

関数の数を増やすだけの目的で、小さな関数を分割しないでください。ユーザーが動作の変更を明示的に求めていない限り、外部 ABI、実行順序、リバート動作、状態遷移の順序、発行されるイベント、資金フローの不変条件を維持してください。

プロジェクトのディレクトリ構成

src/              — Contract source code
  interfaces/     — Interface definitions (I*.sol)
  abstract/       — Shared stateful base contracts (AssetRescue.sol, etc.)
  common/         — Shared constants, types, errors (Const.sol, Types.sol)
test/             — Test files (*.t.sol)
script/           — Deployment & interaction scripts (*.s.sol)
config/           — Network config, parameters (*.json)
deployments/      — Deployment records (latest.env)
docs/             — Documentation, changelogs
lib/              — Dependencies (managed by Foundry)

設定管理

  • config/*.json — ネットワーク RPC URL、コントラクトアドレス、業務パラメーター
  • deployments/latest.env — 最新のデプロイ済みコントラクトアドレス。デプロイのたびに更新する必要があります
  • foundry.toml — コンパイラーバージョン、オプティマイザー設定、リマッピング
  • 重要な設定変更は PR の説明に記載する必要があります

OpenZeppelin ライブラリの選定基準

Solidity コントラクトを記述する際は、独自実装よりも実績のある OpenZeppelin ライブラリを優先して使用してください。状況に応じて、適切なライブラリを選択してください。

アクセス制御
状況ライブラリインポートパス
単一オーナーによる管理Ownable@openzeppelin/contracts/access/Ownable.sol
オーナーの移転に安全性が必要Ownable2Step@openzeppelin/contracts/access/Ownable2Step.sol
複数のロールによる権限管理(admin/operator/minter)AccessControl@openzeppelin/contracts/access/AccessControl.sol
ロールのメンバーを列挙する必要があるAccessControlEnumerable@openzeppelin/contracts/access/AccessControlEnumerable.sol
タイムロック遅延を伴うガバナンスTimelockController@openzeppelin/contracts/governance/TimelockController.sol
オーナーシップモデルの選択

単一オーナーのコントラクトでは、開発者はコード生成の前にオーナーシップモデルを選択する必要があります。

選択肢動作使用する状況
Ownable1 回のトランザクションで所有権の移転が完了しますシンプルさと即時の移転が明示的に優先される場合
Ownable2Step新しいオーナーが acceptOwnership() を呼び出す必要があります誤ったアドレスへの所有権移転を防ぐことが優先される場合

Ownable2Step を暗黙に強制しないでください。プロジェクトで選択が記録されていない場合は、両方の選択肢を提示して開発者に選んでもらってください。選択した後は、共有ベースコントラクト、デプロイスクリプト、テスト、所有権移転コマンドを含め、プロジェクト全体で一貫してそのモデルを使用してください。最初から選択した継承とインポートを生成してください。Ownable2Step を生成し、開発者に手動で置き換えさせないでください。

ルール:ロールが 1 つの場合 → 開発者が Ownable または Ownable2Step を選択。ロールが 2 つ以上の場合 → AccessControl。ガバナンス/DAO → TimelockController

セキュリティ保護
シナリオライブラリ使用方法
外部呼び出し/トークンの送金ReentrancyGuardnonReentrant 修飾子を追加します
緊急時に一時停止が必要Pausableユーザー向け関数に whenNotPaused を追加し、管理者関数は一時停止の対象外にします
ERC20 トークンとのやり取りSafeERC20生の呼び出しではなく、safeTransfer / safeTransferFrom / safeApprove を使用します

ルール:トークンまたは ETH を送金するコントラクトでは、必ず ReentrancyGuard と SafeERC20 を使用してください。

トークン規格
シナリオライブラリ備考
代替可能トークンERC20基本規格
バーン機能付きトークンERC20Burnableburn() と burnFrom() を追加します
最大供給量の上限付きトークンERC20CappedtotalSupply <= cap を強制します
ガス代不要の承認(EIP-2612)ERC20Permitユーザーの approve トランザクションのガス代を節約します
ガバナンス投票トークンERC20Votesスナップショットベースの投票権
NFTERC721基本的な NFT 規格
列挙機能付き NFTERC721EnumerabletokenOfOwnerByIndex による照会をサポートします
マルチトークン(FT と NFT の混在)ERC1155ゲームアイテム、一括操作
ユーティリティライブラリ
シナリオライブラリ使用方法
ホワイトリスト/エアドロップの検証MerkleProofガス効率の高いマークルツリー検証
署名の検証ECDSA + EIP712オフチェーンで署名し、オンチェーンで検証します
自動インクリメント IDCountersトークン ID、注文 ID の生成
関数呼び出しの一括実行Multicall1 つのトランザクションで複数の操作を実行します
アドレスセット/uint セットEnumerableSetO(1) で追加/削除/存在確認ができる反復可能なセット
収益分配PaymentSplitterETH/トークンの支払いを持分に応じて分割します
標準化された利回りボールトERC4626DeFi ボールト規格
コントラクトのアップグレード
シナリオライブラリ備考
アップグレード可能なコントラクト(ガス効率重視)UUPSUpgradeable実装コントラクトにアップグレードロジックを配置します
アップグレード可能なコントラクト(管理者を分離)TransparentUpgradeableProxyプロキシにアップグレードロジックを配置し、ガス代は高くなります
イニシャライザー(コンストラクターの代替)Initializableコンストラクターの代わりに initializer 修飾子を使用します

ルール:新しいプロジェクトでは UUPSUpgradeable を優先し、アップグレード可能なコントラクトでは必ず Initializable を使用してください。

オラクルとオフチェーンサービス
シナリオライブラリ備考
トークン価格データAggregatorV3Interfaceオラクルのデータフィードがサポートされているトークンにのみ使用します
検証可能な乱数(宝くじ/NFT)VRFConsumerBaseV2オンチェーンで公平性を証明できる乱数
自動実行(cron ジョブ)AutomationCompatible中央集権型のキーパーを置き換えます
クロスチェーンメッセージングCCIPクロスチェーンでのトークン/メッセージ転送
ライブラリ選択の判断フロー
Does contract handle user funds/tokens?
├── YES → Add ReentrancyGuard + SafeERC20
│         Does it need emergency stop?
│         ├── YES → Add Pausable
│         └── NO  → Skip
└── NO  → Skip

How many admin roles needed?
├── 1 role  → Ask developer to choose Ownable or Ownable2Step
├── 2+ roles → AccessControl
└── DAO/governance → TimelockController

Does contract need price data?
├── Token has oracle feed → AggregatorV3Interface
├── No oracle feed → Custom TWAP with min-liquidity check
└── No price needed → Skip

Will contract need upgrades?
├── YES → UUPSUpgradeable + Initializable
└── NO  → Standard deployment (immutable)
アンチパターン(禁止事項)
  • 禁止:独自の transfer ラッパーを書かないでください。SafeERC20 を使用してください。
  • 禁止:独自のアクセス制御修飾子を書かないでください。Ownable / AccessControl を使用してください。
  • 禁止:独自の一時停止ロジックを書かないでください。Pausable を使用してください。
  • 禁止:Solidity >= 0.8.0 で SafeMath を使用しないでください。オーバーフローチェックは組み込みです。
  • 禁止:require(token.transfer(...)) を使用しないでください。SafeERC20 を介して token.safeTransfer(...) を使用してください。
  • 禁止:認証に tx.origin を使用しないでください。Ownable / AccessControl とともに msg.sender を使用してください。

MCP を利用したコントラクト生成(利用可能な場合)

OpenZeppelinContracts MCP が設定されている場合は、ゼロから作成するのではなく、ベースコントラクトの生成に使用してください。

コントラクトの種類MCP ツール使用するタイミング
代替可能トークンsolidity-erc20新しい ERC20 トークンコントラクトを作成する場合
NFTsolidity-erc721新しい NFT コントラクトを作成する場合
マルチトークンsolidity-erc1155ゲームアイテム、一括操作
ステーブルコインsolidity-stablecoinERC20 に準拠したステーブルコイン
実世界資産solidity-rwa資産のトークン化
スマートアカウントsolidity-accountERC-4337 のアカウント抽象化
ガバナンスsolidity-governorDAO の投票と提案
カスタムsolidity-customOZ パターンを使った標準外のコントラクト

ワークフロー:MCP でベースを生成 → このスキルの命名規則と構造ルールを適用 → ビジネスロジックをカスタマイズ → /solidity-security のルールを適用

手動作成より MCP を使う理由:MCP の出力は OZ Contracts Wizard と同じルールセットに照らして検証されます。インポート、修飾子、セキュリティチェックが確実に正しく設定されます。手動でコーディングすると、インポートを忘れたり、誤った OZ バージョンを使用したりするリスクがあります。

MCP を使用しない場合:標準外のパターンを使う高度にカスタマイズされたコントラクト、OZ のテンプレートに当てはまらないコントラクト、または最初の行から細かく制御する必要がある場合です。

フォールバック:MCP が設定されていない場合は、上記の Library Selection Standards に従い、このスキルのすべてのルールに沿って手動でコントラクトを作成してください。

Foundry クイックリファレンス

操作コマンド
新しいプロジェクトを作成forge init <project-name>
依存関係をインストールforge install openzeppelin-contracts
コントラクトをビルドforge build
コードをフォーマットforge fmt
remappings を更新forge remappings

コメント