solidity-governance
solidity-governance: DAO・ガバナンス開発スキル
DAO ガバナンスにおける投票、提案、タイムロック、トレジャリー管理のパターンを提供するドメイン特化スキル。
対象
- OpenZeppelin Governor ベースのガバナンス
- 投票メカニズム(トークン加重・Quadratic Voting・veToken)
- タイムロック・遅延実行
- トレジャリー管理・マルチシグ
ワークフロー
Step 1: ガバナンス要件の整理
ユーザーの要件からガバナンスの構成要素を判定する:
| ユースケース | リファレンス | 読み込み時の注目ポイント |
|---|---|---|
| DAO 構築・投票システム | references/dao-patterns.md |
Governor 設計、投票メカニズム選択、提案ライフサイクル |
| トレジャリー・資金管理 | references/treasury-patterns.md |
マルチシグ、予算制限、支出承認フロー |
| 遅延実行・安全装置 | references/timelock-patterns.md |
TimelockController、緊急停止、ガーディアン |
複数のコンポーネントが関連する場合(Governor + Timelock が一般的)は全てのリファレンスを読み込む。
検証ゲート: ガバナンスの基本要件(投票方式・トークン種別・意思決定スコープ)が明確であること。不明な場合は AskUserQuestion で確認する。
Step 2: ガバナンストークンの決定
| 方式 | 推奨実装 | ユースケース |
|---|---|---|
| ERC20 ベース | ERC20Votes(投票権委任対応) |
一般的な DAO。トークン保有量に比例した投票権 |
| NFT ベース | ERC721Votes(1 NFT = 1票) |
メンバーシップ型 DAO。1人1票の平等な投票 |
| 既存トークン活用 | ERC20Wrapper + ERC20Votes |
既存の ERC20 トークンに投票機能を追加 |
| ロック型 | veToken パターン(自前実装) | 長期保有を優遇。ロック期間に応じて投票権増加 |
判断が不明な場合: AskUserQuestion で「既存トークンの有無」「投票権の平等性」「ロックインセンティブの要否」を確認する。
検証ゲート: ガバナンストークンの方式が決定し、OpenZeppelin の対応ライブラリが利用可能であること。
Step 3: Governor パラメータ設計
プロジェクトの規模・性質に応じてパラメータを設計する:
| パラメータ | 小規模 DAO(<100人) | 中規模 DAO(100-1000人) | 大規模 DAO(1000+人) |
|---|---|---|---|
| Voting Delay | 1 day (7200 blocks) | 2 days | 3 days |
| Voting Period | 3-5 days | 7 days | 14 days |
| Proposal Threshold | 0.1-1% of supply | 0.5-2% | 1-5% |
| Quorum | 2-5% | 4-10% | 10-20% |
| Timelock Delay | 1-2 days | 2-3 days | 3-7 days |
設計の考慮事項:
- Voting Delay: フラッシュローン投票攻撃の防止。スナップショットは提案作成時のブロックで取得されるため、Delay 期間中のトークン購入は投票に反映されない。
- Proposal Threshold: スパム提案の防止。低すぎると攻撃コスト低下、高すぎると少数派の提案が困難。
- Quorum: 投票の正当性確保。低すぎると少数派による支配、高すぎると可決が困難。
- Timelock Delay: 不利な提案が実行される前にユーザーが退出(exit)する時間を確保。
検証ゲート: 全パラメータが設定され、矛盾がないこと(例: Voting Delay < Voting Period)。
Step 4: コード生成
solidity-coreのlanguage-patterns.mdに従い NatSpec・コーディング規約を適用する。- ガバナンス固有のコンポーネントを生成する:
- Governor コントラクト: OpenZeppelin の Governor + 必要な拡張を継承
- ガバナンストークン:
ERC20VotesまたはERC721Votes - TimelockController: Governor の executor として設定
- デプロイスクリプト: 正しいデプロイ順序(Token → Timelock → Governor → Role 設定)
- テストコードを同時に生成する:
- 提案 → 投票 → キュー → 実行の完全フロー
- Quorum 未達テスト
- Proposal Threshold 未達テスト
- タイムロック遅延テスト
検証ゲート: forge build がエラーなく完了し、提案→投票→実行のフローテストがパスすること。
Step 5: セキュリティ確認
ガバナンス特有のセキュリティリスクを確認する:
- フラッシュローン投票:
ERC20Votesのスナップショット投票が有効か。提案時点のgetPastVotesを使用しているか。 - ガバナンス乗っ取り: Proposal Threshold と Quorum が攻撃コストに対して十分か。
- 提案スパム: Proposal Threshold が設定されているか。
- 実行操作のリスク: Timelock の遅延期間が十分か。緊急停止のガーディアンが設定されているか。
- 投票権の集中: 大口保有者が Quorum を単独で満たせないか確認。
検証ゲート: CRITICAL レベルのセキュリティ問題が 0 件であること。
使用例
例 1: OpenZeppelin Governor ベースの DAO
ユーザー入力: 「トークン保有者が提案・投票できる DAO を作りたい。タイムロック付きで」
アクション:
- Step 1: DAO 構築 + タイムロック →
dao-patterns.md+timelock-patterns.mdを読み込み - Step 2: ERC20 ベース →
ERC20Votesを選択 - Step 3: 中規模 DAO のパラメータを適用(Voting Delay: 2日、Period: 7日、Quorum: 4%)
- Step 4: 以下を生成:
src/GovernanceToken.sol—ERC20Votes+ERC20Permit継承。ミント関数付きsrc/MyGovernor.sol—Governor+GovernorSettings+GovernorCountingSimple+GovernorVotes+GovernorVotesQuorumFraction+GovernorTimelockControl継承src/MyTimelock.sol—TimelockControllerラッパーtest/Governance.t.sol— 提案→投票→キュー→実行のフルフロー、Quorum 未達、期限切れテストscript/DeployGovernance.s.sol— Token → Timelock → Governor の順序でデプロイ。Timelock に Governor を proposer / executor として設定
- Step 5: フラッシュローン防御(スナップショット投票)、タイムロック遅延の確認
結果: 完全なガバナンスシステム(トークン + Governor + Timelock)がテスト・スクリプト付きで生成される。
例 2: NFT ベースの 1人1票 DAO
ユーザー入力: 「メンバーシップ NFT を持っている人が投票できる DAO。1人1票にしたい」
アクション:
- Step 1: DAO 構築 →
dao-patterns.mdを読み込み → NFT 投票パターンを選択 - Step 2: NFT ベース →
ERC721Votesを選択(solidity-nftのトークン標準も参照) - Step 3: 小規模 DAO パラメータ(Voting Delay: 1日、Period: 5日、Quorum: 5%)
- Step 4: 以下を生成:
src/MembershipNFT.sol—ERC721Votes継承。招待制ミント(owner のみ)src/NFTGovernor.sol— NFT ベース Governortest/NFTGovernance.t.sol— NFT 保有者の投票テスト、非保有者の投票拒否テスト
- Step 5: 1人が複数 NFT を保有した場合の投票権確認(NFT 数 = 投票権数)
結果: メンバーシップ NFT による投票システムが生成される。
例 3: マルチシグ + Governor ハイブリッド
ユーザー入力: 「初期は少人数のマルチシグで運営して、将来的にフルオンチェーンガバナンスに移行したい」
アクション:
- Step 1: トレジャリー + DAO →
treasury-patterns.md+dao-patterns.mdを読み込み - Step 2: 初期は既存トークンなし → まずマルチシグ、後で ERC20Votes を導入
- Step 3: 段階的移行パラメータ設計
- Step 4: 以下を生成:
- Phase 1:
src/Treasury.sol—TimelockControllerベース。3/5 マルチシグを proposer / executor に設定 - Phase 2(移行用):
src/GovernanceToken.sol+src/MyGovernor.sol— Governor を Timelock の proposer に追加し、マルチシグを段階的に executor から削除 test/Migration.t.sol— Phase 1 → Phase 2 の移行テスト
- Phase 1:
- Step 5: 移行中のセキュリティ(マルチシグと Governor が同時に権限を持つ期間の管理)
結果: 段階的にオンチェーンガバナンスへ移行可能なハイブリッドシステムが生成される。
トラブルシューティング
1. 提案が実行できない(タイムロックエラー)
症状: 投票成功後、queue または execute が AccessControl: account is missing role でリバート
原因と対策:
- Governor に Timelock の role が設定されていない: デプロイ後に
TimelockController.grantRole(PROPOSER_ROLE, governorAddress)とgrantRole(EXECUTOR_ROLE, governorAddress)を実行する必要がある。デプロイスクリプトに含めること。 - Timelock の delay が経過していない:
queue後、executeには Timelock のminDelay以上の時間経過が必要。テストではvm.warp(block.timestamp + minDelay + 1)でスキップする。
2. Quorum が達成できない
症状: 十分な票数があるのに提案が Defeated になる
原因と対策:
- 投票権の委任忘れ:
ERC20Votesはデフォルトで投票権が 0。トークン保有者はdelegate(自分のアドレス)を呼ぶ必要がある。フロントエンドで自動委任を実装するか、_afterTokenTransferで自動委任する。 - スナップショットのタイミング: 投票権は提案作成時のブロックで確定する。提案作成後にトークンを取得しても投票権に反映されない。
- Quorum 計算の対象:
GovernorVotesQuorumFractionはtoken.getPastTotalSupply()に対するパーセンテージ。トークン総供給量が大きすぎると Quorum 達成が困難。
3. 提案の作成ができない
症状: propose が GovernorInsufficientProposerVotes でリバート
原因と対策:
- Proposal Threshold 未達: 提案者の投票権が
proposalThreshold()未満。投票権を委任済みか確認する。 - 投票権の委任タイミング:
delegateのタイミングが提案作成と同一ブロック内だと反映されない。少なくとも 1 ブロック前に委任する。
4. 投票結果が反映されない
症状: castVote が成功するが、投票結果に反映されない
原因と対策:
- 投票期間外:
state()がActiveでない期間に投票している。votingDelay経過後、votingPeriod内に投票する。テストではvm.roll(block.number + votingDelay + 1)でスキップ。 - 重複投票: 同一アドレスからの 2 回目の投票は無視される(エラーにならない場合がある)。
hasVotedで確認する。
5. デプロイ順序の問題
症状: Governor / Timelock の参照がゼロアドレスになる
原因と対策:
- 循環依存: Governor は Timelock を、Timelock は Governor を参照する。正しいデプロイ順序: (1) Token → (2) Timelock(Governor = address(0) で仮設定)→ (3) Governor(Timelock 参照)→ (4) Timelock に Governor の role を付与。
- Deployer の admin role: デプロイ後、deployer が Timelock の
DEFAULT_ADMIN_ROLEを放棄(renounceRole)することを忘れない。放棄しないとデプロイヤーが全権を持ち続ける。
注意事項
- ガバナンス攻撃(フラッシュローン投票)への防御としてスナップショット投票(
ERC20Votes)を使用する。 - タイムロックは提案実行前の監視期間として不可欠。最低 1 日以上を推奨。
- 初期段階ではマルチシグ + ガバナンスの併用を推奨する。フルオンチェーンガバナンスは十分にコミュニティが成熟してから移行する。
- 投票権の自動委任(
_afterTokenTransfer内で_delegate)を検討する。委任忘れは最も一般的な UX 問題。 - 基盤的なパターン(アクセス制御、ガス最適化等)は
solidity-coreを参照する。 - フロントエンド統合(提案 UI、投票 UI)は
web3-frontendを参照する。 - OpenZeppelin Governor の利用を推奨し、カスタムガバナンスの独自実装は避ける。