A コードを通じて集団的な意思決定を可能にするシステムであり、取締役会、CEO、CTOなどの伝統的な組織の階層に依存することなく、個人や機関への信頼の代わりに、DAOは ブロックチェーンに展開。 DAO (Decentralized Autonomous Organization) smart contracts その中心に、DAOは参加者に、 で、 そして、 透明で検証可能な方法で、投票権は通常、 参加者によって維持され、各トークンは投票重量の単位を表します。 propose vote execute decisions tokens A typical on-chain DAO is composed of three main smart contracts: トークン契約:統治トークンを定義し、投票権を追跡します。 知事契約:提案と投票の論理を管理する:誰が提案できるか、どのように投票が数えられるか、定数要件、および提案の結果。 タイムロック契約:提案の承認と実行の間の遅延を強制し、参加者が潜在的に有害な決定に反応する時間を与えることによって、セキュリティの層として機能します。 提案のライフサイクルはシンプルだが強力である:提案は提案に提出される。 投票はトークンの所有権に基づいて収集され、提案が承認されると、それはトークンの所有権に転送されます。 遅れた実施のため、提案が失敗した場合、それは単に廃棄されます。 Governor Timelock この記事シリーズでは、ダオを地上から使用して構築します。 この部分( , we will focus on writing, deploying, and testing the わたしたちが呼ぶもの このトークンは後でDAOにおけるオンチェーン投票と意思決定を可能にするために使用されます。 オープニング Part 1) governance token GovernanceToken オープニング トークンコード さらなるアドオなしに、以下はコードです。 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol"; contract GovernanceToken is ERC20, ERC20Permit, ERC20Votes, Ownable { constructor() ERC20("GovernanceToken", "MGT") ERC20Permit("GovernanceToken") Ownable(msg.sender) { _mint(msg.sender, 1_000_000 * 10 ** decimals()); } // Optional: Add controlled minting function mint(address to, uint256 amount) external { require(msg.sender == owner(), "Only owner can mint"); _mint(to, amount); } // ── Conflict resolution ── // Both ERC20 and ERC20Votes define _update function _update(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) { super._update(from, to, amount); } // Both ERC20Permit and Nonces define nonces() function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) { return super.nonces(owner); } } 従来のERC20トークンと比べると、 さらに2つのOpenZeppelinモジュールが組み込まれています。 そして . GovernanceToken ERC20Permit ERC20Votes ERC20Votes は、特に getPastVotes(アカウント、ブロックナンバー) を含む統治特有の機能を追加します。この機能は、DAO コンテキストでは、このスナップショットメカニズムは重要です:投票力は提案が作成された瞬間に固定され、ユーザーが事実の後にトークンを購入または転送することによって投票を操作するのを防ぎます。 ERC20Permitは、署名(EIP-2612)を通じてガスフリーの承認を可能にし、ユーザーはチェーン上の取引を送信することなく投票権を委任または承認することができます。 最も重要な論理は、 , which initializes all inherited modules and mints one million governance tokens to the developer. We also define an optional 契約の所有者に限定された機能は、展開後にコントロールされたトークンの発行を可能にする(テストや将来のガバナンス決定に役立つ)。 constructor mint 最後に、二つの機能――。 そして — は明示的に上記されなければならない。これは、複数の親契約で定義されているため必要である。 すべての継承された行動が正しく構成され、コンパイラの継承紛争がきれいに解決されることを保証する。 _update nonces super トークンを構築する 私たちの統治トークンを構築するために、私たちは使用します。 次のステップは、Linux環境を仮定しますが、ワークフローはmacOSでも同様です。 Foundry 私たちは、公式のインストールスクリプトを使用してFoundryをインストールすることから始めます: curl -L https://foundry.paradigm.xyz | bash インストール後、スクリプトは私たちのシェル環境を更新し、Foundry バイナリをインストールするように指示します。 source ~/.bashrc # path may vary depending on your system foundryup This installs the full Foundry toolchain: すべてのツールチェーンをインストールします。 (ビルド&テスト) (CLIインタラクション) (地元ノード)および (レッスン) forge cast anvil chisel 次に、空のディレクトリに新しい Foundry プロジェクトを初期化します。 mkdir DAO cd DAO forge init これにより、完全なプロジェクトシェルターを生み出し、 で、 そして、 デフォルトでは、Foundry が例を作成します。 プロジェクト構造だけを望んでいるので、これらのサンプルファイルを安全に削除し、独自の契約で置き換えることができます。 src/ script/ test/ カウンター 今のところ、我々は我々の統治トークンを追加する。 : src/ src/ └── GovernanceToken.sol (内容を含む) 前項で定められた条項です)。 GovernanceToken 私たちのトークンはOpenZeppelinモジュールに依存しているため、OpenZeppelin契約ライブラリをインストールする必要があります。 forge install OpenZeppelin/openzeppelin-contracts このコマンドは、OpenZeppelinを ディレクトリおよび当社のプロジェクト内の輸入のために契約を提供します。 lib/ 最後に、プロジェクトをまとめます: forge build すべてが正しく設定されている場合、コンパイルは成功して完了し、 このフォルダには、コンパイルされたアーティファクト(ABIとバイトコード)が含まれています。 また、すべてのOpenZeppelin依存性の継承も同様です。 out/ GovernanceToken この時点で、私たちのガバナンストークンは完全にコンパイルされ、展開およびテストの準備ができています - 私たちが次のセクションでカバーするステップ。 「Token」の展開 統治トークンが組み立てられると、今ではそれをローカルブロックチェーンに展開することができます。 . deployment scripts デプロイス・スクリプトを作成することから始めます。 Under The ディレクター: DeployGovernanceToken.s.sol script/ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Script} from "forge-std/Script.sol"; import {GovernanceToken} from "../src/GovernanceToken.sol"; contract DeployGovernanceToken is Script { function run() external { vm.startBroadcast(); new GovernanceToken(); vm.stopBroadcast(); } } このシナリオはA Foundry が実行する機能. The / / pair は Foundry に、トランザクションをシミュレーションするのではなく、ネットワークに送信するように指示します。 run vm.startBroadcast() vm.stopBroadcast() 次に、使用するローカルEthereumネットワークを立ち上げます。 (別個のターミナル内): Anvil anvil Anvil は、ローカルノードを起動します。 これらのアカウントは、開発およびテストのみを目的としており、プライベートキーとともに、事前資金調達されたアカウントのリストを印刷します。 http://127.0.0.1:8545 Anvil が実行されると、我々は契約を使用して展開することができる。 : forge script forge script script/DeployGovernanceToken.s.sol \ --rpc-url http://127.0.0.1:8545 \ --broadcast \ --private-key <ANVIL_PRIVATE_KEY> RPC URLとプライベートキーはAnvilの出力から直接取られ、コマンドが成功すると、Foundryはトランザクションハッシュ、展開された契約アドレス、ガス使用量、および契約が作成されたブロック番号を印刷します。 展開がうまくいったことを迅速に確認するには、展開契約をクエリすることができます。 例えば、呼び出し 最初のミントが予想通りに発生したことを確認します: cast totalSupply() cast call <DEPLOYED_CONTRACT_ADDRESS> \ "totalSupply()(uint256)" \ --rpc-url http://127.0.0.1:8545 返される値は、 )は、構築者に刻まれた金額に匹敵する。 1,000,000 tokens with 18 decimals (1000000000000000000000000 [1e24] この段階では、当社のガバナンストークンは、ローカルネットワーク上で生存しており、投票、代表、および最終的にDAOのガバナンスのテストに使用する準備ができています。 トークンをテストする 統治トークンの行動を検証するには、単位テストを書くことができます。 Foundry のテスト フレームワーク. Tests live in the ディレクトリと、 Solidity で書かれている。 forge-std test/ 以下は、検証する簡単なテストです。 予想通りに機能する: mint // test/GovernanceToken.t.sol pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {GovernanceToken} from "../src/GovernanceToken.sol"; contract TokenTest is Test { GovernanceToken token; function setUp() public { token = new GovernanceToken(); } function testMint() public { uint256 before = token.balanceOf(address(this)); token.mint(address(this), 100); uint256 after_ = token.balanceOf(address(this)); assertEq(after_ - before, 100); } } THE 機能は各テストの前に実行され、新しいインスタンスを展開します。 テストケースの間の隔離を確保する。 機能はその後、呼び出しをチェックする 受取人の予想額を増やします。 setUp GovernanceToken testMint mint テストスイートを実行することは、以下のようにシンプルです。 forge test Foundry は契約を編集し、テストを実行し、結果を報告します。 結論 この記事では、DAOの最初のビルドブロックに取り組んだ: 私たちはトークン契約自体を検討し、OpenZeppelinモジュールから受け継がれるオープンゼッペリンモジュールとそれらが提供する追加のガバナンス関連機能に特に注意を払って始めました。 governance token その後、完全な開発ワークフローを通じて歩きました。 プロジェクトを初期化し、トークンをローカルAnvilネットワークに展開し、最後にユニットテストでその行動を検証する。 Foundry この統治トークンは、次に続くすべてのための基盤として機能します。このシリーズの次の部分では、このトークンを完全に機能するチェーン上のDAOに変える代表、投票メカニズム、およびコアの統治契約を導入することによってその上に構築します。 私はあなたがこの記事が役に立つことを望んでいることを願っています. Feel free to like, share, and subscribe for more content in the series. Miscellaneous:Extra Commands(エキストラコマンド) この記事に示すすべてのコマンドは、次のコマンドで作成された Docker コンテナ内で実行されました。 docker run -it ubuntu:ubuntu@sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782 ピンデッド画像ダイジェストを使用すると、 環境は、コンテナがいつ、どこで打ち上げられるかに関係なく、常に同じです。 full reproducibility 2 走る 別々のターミナルで、我々は単に同じコンテナに接続した: Anvil docker exec -it <CONTAINER_NAME> bash anvil 変数 コマンドを通じて見つけることができます: コンテナ名(コンテナ名) $ docker ps Foundry では、デプロイス スクリプトを実行することもできます。 次のコマンドはシミュレート環境でスクリプトを実行し、ガス使用量を報告し、いかなるトランザクションも放送しない。 without forge script script/DeployGovernanceToken.s.sol --broadcast このモードは、展開論理を迅速に検証し、ガスコストを推定するのに役立ちます。実際のネットワーク(ローカルまたはリモート)に対してトランザクションをシミュレートまたは実行したい場合は、RPC URL を提供するだけです。 旗です。 --rpc-url Miscellaneous: 警告 開発中に、独自の契約ではなく、依存に関連する警告に遭遇する場合があります。 図書館: lib/forge-std Warning (2424): Natspec memory-safe-assembly special comment for inline assembly is deprecated and scheduled for removal. Use the memory-safe block annotation instead. --> lib/forge-std/src/StdStorage.sol:301:13 これらの警告はAによって引き起こされる。 Solidity コンパイラとインストールされたバージョンの間で . Newer Solidity versions deprecate the NatSpecが賛成のコメント ブロックの注釈は、古いライブラリのバージョンは依然として削除された構文を使用する場合があります。 version mismatch forge-std @memory-safe-assembly memory-safe 問題は依存性から生じるので、最も簡単な修正は、更新することです。 最新バージョンへ: forge-std cd lib/forge-std git pull origin master git checkout master cd - ライブラリを更新すると、警告が消え、プロジェクトが再びクリーンにコンパイルされます。 これは、コンパイラの警告が常に独自のコードによって引き起こされるわけではないという良い思い出です.When working with rapidly evolving toolchains such as Foundry and Solidity, keeping dependencies up-to-date is often necessary to avoid noisy or misleading warnings. コンパイラの警告を常に自分のコードによって引き起こすわけではありません。