solidity-deploy について
solidity-deployは、FoundryでSolidityコントラクトをデプロイ・アップグレードする開発者向けのワークフロースキルです。事前・事後チェック、署名とブロードキャストの安全規則に加え、アップグレード可能なコントラクト向けのOpenZeppelin Foundry Upgrades Pluginの指針を提供します。
主な機能
- 01Foundryのデプロイ前チェックリスト
- 02ドライランとデプロイ後の手順
- 03署名とブロードキャストの安全規則
- 04OpenZeppelinプロキシデプロイの指針
- 05アップグレード検証とストレージ確認
活用シーン
- 01Foundryコントラクトのデプロイ準備
- 02ブロードキャスト前のデプロイシミュレーション
- 03アップグレード可能なプロキシのデプロイや更新
- 04デプロイ設定とオンチェーン状態の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ 0xlayerghost/ solidity-agent-kit --skill solidity-deployインストール後、Claude Code で/solidity-deployを入力して読み込まれたか確認します。
詳細
デプロイワークフロー
言語ルール
- 常にユーザーが使用している言語で応答してください。 ユーザーが中国語で質問した場合は、中国語で応答してください。英語で質問した場合は、英語で応答してください。
デプロイ前チェックリスト(すべて合格する必要があります)
| 手順 | コマンド/アクション |
|---|---|
| コードをフォーマットする | forge fmt |
| すべてのテストを実行する | forge test — 失敗がゼロであること |
| ガスレポートを確認する | forge test --gas-report — 重要な関数を確認する |
| 設定を検証する | config/*.json のパラメーターを手動で確認する |
| ドライランする | forge script <Script> --fork-url <RPC_URL> -vvvv(--broadcast は付けない) |
| 残高を確認する | cast balance <DEPLOYER> --rpc-url <RPC_URL> — 十分なガスがありますか? |
| ガス上限を設定する | デプロイコマンドに --gas-limit を含める必要があります |
デプロイの判断ルール
| 状況 | ルール |
|---|---|
| デフォルトのデプロイ | --verify を付けないでください — デフォルトでは、コントラクトはブロックエクスプローラーで検証されません |
| ユーザーが検証を要求した場合 | コマンドに --verify と --etherscan-api-key を追加します |
| デプロイ後の検証 | 別の手順として forge verify-contract を使用します |
| マルチチェーンデプロイ | チェーンごとに別のスクリプトを使用し、複数のチェーンを1つのスクリプトでまとめて処理しないでください |
| プロキシのデプロイ | 先に実装をデプロイし、次にプロキシをデプロイします。両方を個別に検証してください |
| アップグレード可能なコントラクト | OpenZeppelin Upgrades Plugin を使用してください(以下を参照)。プロキシのデプロイスクリプトを独自に作成しないでください |
デプロイ後の操作(すべて必須)
config/*.jsonとdeployments/latest.envのアドレスを更新します- 重要な関数をテストします。
cast callを使って、オンチェーンの状態が正しいことを確認します - 変更を
docs/CHANGELOG.mdに記録します - デプロイトランザクションのハッシュへのリンクを添えて PR を提出します
- 検証が必要な場合は、別途
forge verify-contractを実行します
重要なセキュリティルール
- ブロードキャストする前に、開発者に署名方法を選択してもらいます。
- Foundry Keystore(推奨):
--account <KEYSTORE_NAME>を使用します。 - 環境ファイル:
PRIVATE_KEYを Git で無視される.envに保存し、source .envを実行してから、--private-key "$PRIVATE_KEY"を使用します。
- Foundry Keystore(推奨):
- コマンド、スクリプト、ログ、会話、コミットするファイルに、実際の秘密鍵を記載しないでください。 開発者の
.envを読み取ったり、内容を表示したりしないでください。 - テンプレートに
--broadcastを含めないでください。 デプロイの準備ができたら、ユーザーが明示的に追加する必要があります。
コマンドテンプレート
# Dry-run (simulation only, no on-chain execution)
forge script script/Deploy.s.sol:DeployScript \
--rpc-url <RPC_URL> \
--gas-limit 5000000 \
-vvvv
# When the developer is ready to deploy, ask them to choose one signer:
# Keystore (recommended): add --account <KEYSTORE_NAME> --broadcast
# .env: source .env, then add --private-key "$PRIVATE_KEY" --broadcast
# Verify existing contract separately
forge verify-contract <ADDRESS> <CONTRACT> \
--chain-id <CHAIN_ID> \
--etherscan-api-key <API_KEY> \
--constructor-args $(cast abi-encode "constructor(address)" <ARG>)
# Quick on-chain read test after deployment
cast call <CONTRACT_ADDRESS> "functionName()" --rpc-url <RPC_URL>
アップグレード可能なコントラクトのデプロイ(OpenZeppelin Upgrades Plugin)
アップグレード可能なコントラクト(UUPS、Transparent、Beacon)には、プロキシのデプロイスクリプトを独自に作成するのではなく、OpenZeppelin Foundry Upgrades Plugin を使用してください。
プラグインを使用する理由
| 手動の方法 | プラグインを使用する場合 |
|---|---|
| 約30行:実装をデプロイ → プロキシをデプロイ → 初期化子をエンコード → 接続を設定 | 1行:Upgrades.deployUUPSProxy(...) |
| 約20行:新しい実装をデプロイ → ストレージを検証 → プロキシをアップグレード | 1行:Upgrades.upgradeProxy(...) |
| ストレージレイアウトの互換性:目視で確認 | 自動チェックされ、互換性のないレイアウトは拒否されます |
_disableInitializers() を忘れた場合:警告なし | 自動検証されます |
インストール
forge install OpenZeppelin/openzeppelin-foundry-upgrades
forge install OpenZeppelin/openzeppelin-contracts-upgradeable
remappings.txt に追加します:
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
デプロイスクリプトのテンプレート(UUPS)
// script/Deploy.s.sol
import {Script, console} from "forge-std/Script.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
import {MyContract} from "../src/MyContract.sol";
contract DeployScript is Script {
function run() public {
vm.startBroadcast();
// One line: deploys impl + proxy + calls initialize
address proxy = Upgrades.deployUUPSProxy(
"MyContract.sol",
abi.encodeCall(MyContract.initialize, (msg.sender))
);
console.log("Proxy:", proxy);
console.log("Impl:", Upgrades.getImplementationAddress(proxy));
vm.stopBroadcast();
}
}
アップグレードスクリプトのテンプレート
// script/Upgrade.s.sol
import {Script, console} from "forge-std/Script.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
contract UpgradeScript is Script {
function run() public {
address proxy = vm.envAddress("PROXY_ADDRESS");
vm.startBroadcast();
// One line: validates storage layout + deploys new impl + upgrades proxy
Upgrades.upgradeProxy(proxy, "MyContractV2.sol", "");
console.log("Upgraded. New impl:", Upgrades.getImplementationAddress(proxy));
vm.stopBroadcast();
}
}
自動参照のため、V2 コントラクトに @custom:oz-upgrades-from MyContract アノテーションを追加します:
/// @custom:oz-upgrades-from MyContract
contract MyContractV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable {
// ...
}
コマンド
# Deploy proxy (dry-run) — --ffi is required for storage layout checks
forge script script/Deploy.s.sol --rpc-url <RPC_URL> --ffi -vvvv
# Deploy proxy (broadcast) with Foundry Keystore
forge script script/Deploy.s.sol --rpc-url <RPC_URL> --ffi --account <KEYSTORE_NAME> --broadcast
# Or deploy with PRIVATE_KEY from a gitignored .env
source .env
forge script script/Deploy.s.sol --rpc-url <RPC_URL> --ffi --private-key "$PRIVATE_KEY" --broadcast
# Upgrade proxy (dry-run)
PROXY_ADDRESS=0x... forge script script/Upgrade.s.sol --rpc-url <RPC_URL> --ffi -vvvv
# Upgrade proxy (broadcast) with Foundry Keystore
PROXY_ADDRESS=0x... forge script script/Upgrade.s.sol --rpc-url <RPC_URL> --ffi --account <KEYSTORE_NAME> --broadcast
# Or upgrade with PRIVATE_KEY from a gitignored .env
source .env
PROXY_ADDRESS=0x... forge script script/Upgrade.s.sol --rpc-url <RPC_URL> --ffi --private-key "$PRIVATE_KEY" --broadcast
# Validate upgrade without deploying (useful for CI)
# Use Upgrades.validateUpgrade("MyContractV2.sol", opts) in a test
プラグイン API クイックリファレンス
| 関数 | 用途 |
|---|---|
Upgrades.deployUUPSProxy(contract, data) | UUPS プロキシ + 実装 + 初期化をデプロイします |
Upgrades.deployTransparentProxy(contract, admin, data) | Transparent プロキシ + 実装 + 初期化をデプロイします |
Upgrades.upgradeProxy(proxy, newContract, data) | 検証 + 新しい実装のデプロイ + アップグレードを行います |
Upgrades.validateUpgrade(contract, opts) | デプロイせずに検証のみを行います(CI/テスト用) |
Upgrades.getImplementationAddress(proxy) | 現在の実装アドレスを取得します |
Upgrades.prepareUpgrade(contract, opts) | 検証 + 新しい実装をデプロイし、アドレスを返します(マルチシグ用) |
重要なルール
- 常に
--ffiフラグを使用してください — プラグインはストレージレイアウトの検証にこのフラグを必要とします - アップグレードでは常に
--sender <ADDRESS>を追加してください — プロキシの所有者と一致する必要があります。一致しない場合はOwnableUnauthorizedAccountが発生します - スクリプトでは
Upgradesを使用し、テストでのみUnsafeUpgradesを使用してください —UnsafeUpgradesはすべての安全性チェックを省略します - アップグレード時も V1 のソースコードをプロジェクト内に残してください — プラグインがストレージを比較するために必要です。または
@custom:oz-upgrades-fromアノテーションを使用してください - このプラグインが利用できる場合、プロキシのデプロイを独自に実装しないでください — ストレージレイアウトのチェックだけでも重大なバグを防げます
コメント
Markdown 対応