From e058f6706a42975422dbf6caf4abff7f802d9cd9 Mon Sep 17 00:00:00 2001 From: thurendous Date: Wed, 18 Sep 2024 15:14:31 +0900 Subject: [PATCH] add docs in ja --- readmeInJa.md | 461 +++++++++++++++++++++++++++++++++++ script/DeployContracts.s.sol | 60 +++++ 2 files changed, 521 insertions(+) create mode 100644 readmeInJa.md diff --git a/readmeInJa.md b/readmeInJa.md new file mode 100644 index 0000000..d6cce39 --- /dev/null +++ b/readmeInJa.md @@ -0,0 +1,461 @@ +# DAO コミュニティのためのスマートコントラクト + +これは DAO コミュニティのスマートコントラクトのリポジトリです。スマートコントラクトは Solidity で書かれ、Foundry テストフレームワークを使用してテストされています。 + +## はじめに + +これは DAO コミュニティガバナンスのためのスマートコントラクトのリポジトリで、コミュニティのための基本的なツールです。 +コミュニティガバナンスのために分離されたツールがあります。これらはスマートコントラクトに基づいて説明されます。 + +すべてのコントラクトは`src/`フォルダに配置されています。これらはプロトコルのコアコントラクトです。 + +このリポジトリは以下のスマートコントラクトで構成されています: + +- `AmbassadorNft` + - コミュニティのメンバーシップを表す NFT のスマートコントラクト。 + - このスマートコントラクトは他のスマートコントラクトとは独立しています。 + - コントラクトは OpenZeppelin の ERC1155 コントラクトに基づいています。 +- `ERC20UpgradeableTokenV1` + - コミュニティのユーティリティトークンを表す ERC20 トークンのスマートコントラクト。 + - その使用例は、コミュニティのガバナンストークンと交換することです。 + - 他の使用例も可能で、コミュニティによって定義されます。 + - コントラクトは OpenZeppelin の ERC20Upgradeable コントラクトに基づいています。 +- `GovToken` + - コミュニティのガバナンストークンとして機能するスマートコントラクト。 + - このトークンは、譲渡不可能な保有者の投票力を表すために使用されます。 + - コントラクトは OpenZeppelin の ERC20Votes コントラクトに基づいています。 +- `VotingPowerExchange` + - コミュニティの投票力交換のためのスマートコントラクト。 + - このコントラクトは、ユーザーが保有するユーティリティトークンを燃焼してガバナンストークンを取得するために使用されます。 + - このコントラクトはゼロから作成されています。 +- `DaoGovernor` + - コミュニティの DAO ガバナンスのためのスマートコントラクト。 + - このコントラクトは、より分散化された方法でコミュニティの提案を管理するために使用されます。 + - コントラクトは OpenZeppelin の Governor コントラクトに基づいています。 +- `Timelock` + - コミュニティのタイムロックのためのスマートコントラクト。 + - このコントラクトは DaoGovernor コントラクトの所有者になります。 + +## ロール + +システムは異なるロールによって管理されます。ロールはデフォルトの管理者ロールによって管理されます。 + +- DEFAULT_ADMIN_ROLE: デフォルトの管理者ロールはコントラクトの所有者です。 + - これはコントラクト内で最高の権限を持つロールです。 + - 責任: 他のすべてのロールを管理する権限を持ちます。 + - 機能: 他のすべてのロールを付与および取り消すことができます。 +- MANAGER_ROLE: + - 責任: コントラクトを管理し、特別な機能を呼び出すことができます。 + - 機能: `setVotingPowerCap()` +- EXCHANGER_ROLE: 交換ロールは、ガバナンストークンをユーティリティトークンと交換できるロールです。 + - 責任: ガバナンストークンをユーティリティトークンと交換できます。 + - 機能: `exchange()` +- MINTER_ROLE: ミンターロールは新しいトークンを発行できるロールです。 + - 責任: アンバサダー NFT、ユーティリティトークン、ガバナンストークンの新しいトークンを発行できます。 + - 機能: `mint()`, `mintBatch()` +- BURNER_ROLE: バーナーロールはアンバサダー NFT、ユーティリティトークン、ガバナンストークンのトークンを燃焼できるロールです。 + - 責任: トークンを燃焼できます。 + - 機能: `burn()`, `burnBatch()`,`burnByBurner()` +- URI_SETTER_ROLE: URI セッターロールはトークンの URI を設定できるロールです。 + - 責任: トークンの URI を設定できます。 + - 機能: `setURI()` +- UPGRADER_ROLE: アップグレーダーロールはコントラクトをアップグレードできるロールです。 + - 責任: コントラクトをアップグレードできます。 + - 機能: `upgradeToAndCall()` + +## スマートコントラクト + +### `AmbassadorNft.sol` + +これはコミュニティのメンバーシップを表す NFT のスマートコントラクトです。可能な使用例と NFT の機能について説明します。 + +このコントラクトは、現在のシステムの他のスマートコントラクトとは独立しています。 + +#### 可能な使用例と機能の説明 + +この NFT はコミュニティのメンバーシップを表すために使用できます。例えば、メンバーシップ A は NFT1155 の ID 0 で表され、メンバーシップ B は NFT1155 の ID 1 で表されます。NFT を保有する人は誰でもコミュニティのメンバーとみなされます。 + +デフォルトの管理者ロールは、ミンターとバーナーのロールを設定する権利を持ちます。ミンターロールは新しいトークンを発行できるロールです。バーナーロールは指定されたアカウントからトークンを燃焼できるロールです。 + +NFT をバッチで発行および燃焼する機能があります。これらの機能により、ミンターとバーナーのロールはメンバーのために NFT を簡単に発行および燃焼できます。 + +また、NFT の URI はコントラクトのデプロイ後に URI セッターロールによって設定できます。 + +### `ERC20UpgradeableTokenV1.sol` + +これはコミュニティのユーティリティトークンを表す ERC20 トークンのスマートコントラクトです。このユーティリティトークンは譲渡可能です。 + +#### 使用例と機能の説明 + +このトークンはコミュニティのユーティリティトークンとして使用され、ミッションの遂行やコミュニティのための他の活動によって獲得できます。ユーザーはユーティリティトークンを使用してガバナンストークンと交換できます。 + +デフォルトの管理者ロールは、ミンターとバーナーのロールを設定する権利を持ちます。ミンターロールは新しいトークンを発行できるロールです。バーナーロールは指定されたアカウントからトークンを燃焼できるロールです。 + +コントラクト自体は一時停止可能です。ポーザーロールはコントラクトを一時停止および再開できるロールです。コントラクトが一時停止されている場合、トークンの発行と譲渡は無効になります。 + +#### 注意点 + +- コントラクトはアップグレード可能、一時停止可能、および ERC20Permit 準拠です。 + +### `GovToken.sol` + +これはコミュニティのガバナンストークンのスマートコントラクトです。コントラクトは OpenZeppelin の ERC20Votes コントラクトに基づいています。コントラクトは、保有者が燃焼したユーティリティトークンの量も記録します。 + +投票力交換コントラクトのみが、保有者が燃焼したユーティリティトークンの量を更新する権利を持ちます。 + +#### 使用例と機能の説明 + +このトークンは保有者の投票力を表すために使用されます。トークンは譲渡不可能です。同時に、保有者のレベルを表すためにも使用されます。例えば、レベルが高いほど、保有者はより多くの投票力を持ちます。 +例えば、1 トークンを保有することは保有者がレベル 2 であることを意味します。0 トークンを保有することは保有者がレベル 1 であることを意味します。 + +トークンは譲渡不可能です。つまり、トークン保有者は他のアドレスにトークンを譲渡できません。 + +ERC20Permit はここで継承されています。これは、OpenZeppelin のウィザードがそうしているためであり、私たちはそのコードに従いました。これは比較的安全な方法だと考えています。 + +#### 注意点 + +このトークンをプロトコルで利用する際には 2 つのフェーズがあります。フェーズ 1 として開始した後、コミュニティガバナンスは観察下に置かれます。フェーズ 1 が安定し成熟したら、将来的にフェーズ 2 に徐々に移行します。 + +- フェーズ 1: + トークンを使用して保有者の投票力を表します。そして、ガバナンス参加者のトークン残高を使用してオフチェーンでガバナンスを管理します。 + +
+ フェーズ1 +
+ +- フェーズ 2: + トークンを使用して保有者の投票力、つまりレベルを表します。そして、ガバナンス参加者のトークン残高を使用してオンチェーンでガバナンスを管理します。 + +
+ フェーズ2 +
+ +GovToken は、ある意味で達成記録係です。私たちは、達成システムがすべての参加者にとって透明で検証可能であることを確認したいと考えています。そのため、このトークンをアップグレード可能にしませんでした。 + +実際、トークンの投票単位は、実際の投票力としてカウントされるために誰かに委任されなければなりません。その理由は、OpenZeppelin コントラクトのドキュメントに記載されています。 + +> 実際、投票単位は実際の投票としてカウントされるために委任されなければなりません。アカウントが決定に参加したい場合や信頼できる代表者がいない場合は、その投票をアカウント自身に委任する必要があります。 + +### `VotingPowerExchange.sol` + +このコントラクトは、ユーティリティトークンをガバナンストークンと交換するために使用されます。交換は、ガバナンストークンを燃焼し、ユーティリティトークンを発行することで行われます。 + +投票力交換コントラクトは、ガバナンストークンのミンターであり、ユーティリティトークンのバーナーです。 + +#### 使用例と機能の説明 + +このコントラクトには、保有者が燃焼したユーティリティトークンに基づいて、保有者が受け取るべきガバナンストークンの量を計算するいくつかの機能があります。 + +主要な機能は`exchange()`です。この機能は交換者ロールのみが呼び出すことができます。 +交換者ロールは、プロトコル所有者によって管理されることを想定しています。 + +この機能は、まず送信者の署名を含む入力の有効性をチェックします。署名はオフチェーンで生成され、送信者自身によってのみ生成できます。 + +これにより、送信者自身がそのような量のトークンを交換する意図を持っていることを確認します。このようにして、交換者ロールが悪用されるのを防ぎたいと考えています。 + +さらに、この機能は署名のノンスと有効期限をチェックして、署名が新しく有効であることを確認します。 + +その後、保有者が受け取るべきガバナンストークンの量を計算します。 + +計算は、*参考資料*セクションの数学的公式に基づいています。 + +コントラクトは、保有者が取得できるガバナンストークンの量に上限を設定します。 + +誰かが上限以下で、上限を超えるユーティリティトークンを交換する場合、この機能は上限が許可する量のトークンのみを交換します。 + +誰かがすでに上限に達している場合、この機能は交換を許可しません。 + +誰も上限を超える投票力を得ることはできません。 + +計算後、この機能は保有者にガバナンストークンを発行し、保有者からユーティリティトークンを燃焼します。 + +この機能は、GovToken コントラクトで保有者が燃焼したユーティリティトークンの量も更新します。 + +#### 注意点 + +- このコントラクトはゼロから作成されています。 +- 計算の精度のために使用されるいくつかの変数があります。 +- `SafeERC20`は使用されていません。なぜなら、私たちが使用しているトークンコントラクトは安全であることが知られているからです。 + +### `DaoGovernor.sol` + +これはコミュニティの DAO ガバナンスのためのスマートコントラクトです。コントラクトは OpenZeppelin の Governor コントラクトに基づいています。 + +このコントラクトは、コミュニティガバナンスのフェーズ 2 で使用されます。 + +#### 使用例と機能の説明 + +- 基本的なガバナー機能(提案の作成、投票、実行) +- 設定可能なガバナンス設定(投票遅延、投票期間、提案閾値) +- 投票力のための ERC20Votes トークンとの統合 +- 総供給量の一部に基づく定足数要件 +- 遅延実行のためのタイムロック統合 + +主要な特徴とパラメータ: + +- 投票遅延:1 日(提案作成から投票開始までの時間) + - 後で変更可能 +- 投票期間:1 週間(投票フェーズの期間) + - 後で変更可能 +- 提案閾値:1e18 トークン(提案を作成するために必要な最小トークン) + - 後で変更可能 +- 定足数:総トークン供給量の 1%(有効な投票のための最小参加) + +#### 注意点 + +- このコントラクトは、特定の ERC20Votes トークン(おそらく前述の GovToken)と TimelockController と連携するように設計されています。 +- いくつかのパラメータ(投票遅延、投票期間、提案閾値、定足数)は、コントラクトがデプロイされたときのセットアップのためにコンストラクタでハードコードされています。 +- このコントラクトは OpenZeppelin の最新のコントラクト(v5.0.0)を使用しており、最新のセキュリティ機能とベストプラクティスを確保しています。 +- このコントラクトは、コミュニティガバナンスがフェーズ 2 で完全に分散化される準備ができたときに使用されることを想定しています。 + +### `Timelock.sol` + +`Timelock.sol`コントラクトは、OpenZeppelin の`TimelockController`に基づいたタイムロックコントローラーです。 + +このコントラクトは、コミュニティガバナンスのフェーズ 2 で使用されます。 + +#### 使用例と機能の説明 + +主な目的と機能は以下の通りです: + +1. 遅延実行:可決された提案に対して必須の待機期間を提供し、ガバナンスのセキュリティを強化します。 +2. アクセス制御:どのアドレスが操作を提案、実行、またはキャンセルできるかを管理します。提案者または実行者として address(0)が設定されている場合、誰でも提案または実行できます。 +3. 透明性:すべての保留中の操作は公開され、コミュニティメンバーがレビューし反応する時間を与えます。 + +DaoGovernor で提案が可決されると、タイムロックにキューイングされます。所定の遅延後、実際の実行はタイムロックによって行われます。 + +#### 注意点 + +- コントラクトの管理者ロールは最初に設定され、後で取り消すことが推奨されます。 +- 実行者ロールは最初に address(0)に設定できると想定しています。これは誰でも実行できることを意味します。そして、提案者ロールは最初にガバナーコントラクトに設定されます。これはガバナーコントラクトのみが提案する権利を持つことを意味します。 +- テストケースでタイムロックに gov トークンを発行するミンターロールを付与する必要がある理由: + - 提案が可決されると、キュー操作が呼び出され、タイムロックの実際に呼び出される関数は`scheduledBatch()`です。その後、DaoGovernor の`execute()`関数は誰でも呼び出して提案を実行できます。そしてこの関数はタイムロックの`executeBatch()`関数を呼び出して提案を実行します。実際の実行者はタイムロックです。 + +#### ガバナンスフロー + +ユーザー A に gov トークンを発行するためにガバナンスを使用するテストケースのコードに書かれているように、ガバナンスのフローは以下の通りです: + +準備: + +- ユーザー A はいくつかの投票力を持っています。 + +1. 提案の値、ターゲット、コールデータ、説明を準備します。 +2. しばらくして、ユーザー A が提案を作成します:`daoGovernor.propose()`。 +3. いくつかのユーザーが提案に投票します:`daoGovernor.castVote()`。 +4. 提案はタイムロックにキューイングされます:`daoGovernor.queue()`。 +5. 遅延後、誰でもこの関数を呼び出すことができます:`daoGovernor.execute()`。 +6. トークンがユーザー A に発行されます。 + +## 監査について + +### スコープ + +- src/ + - AmbassadorNft.sol + - ERC20UpgradeableTokenV1.sol + - GovToken.sol + - VotingPowerExchange.sol + - DaoGovernor.sol + - Timelock.sol + +### スコープ外 + +上記のスコープにあるファイル以外のすべてのファイル。 + +### 既知の問題 + +- 2 段階の所有権移転プロセスは既知の問題です。私たちはそのリスクを受け入れています。 +- 投票力と燃焼されたトークンの計算における精度の損失は存在しますが、それによって引き起こされる価値の損失は非常に小さく(例:1e9 トークン)、無視できます。 + +### いくつかの注意点 + +- 平方根計算を含む数学的公式(*参考資料*セクションで言及)を使用しているため、計算において精度の損失が発生します。例えば、理論的に 1e9 ユーティリティトークンをガバナンストークンと交換する場合、得られるのは 0 トークンです。そしてユーザーは理由なくシステムにガス料金を消費させるためにリクエストをスパムする可能性があります。これを考慮して、誰かが`exchange`関数を呼び出したい場合、ユーティリティトークンの値を少なくとも 1e18 にしました。 +- テスト(ファジングテストも含む)がすべてのエッジケースをカバーできていない可能性があります。問題を見つけた場合は、お知らせください。 + +## テスト + +テストは`test/`ディレクトリに書かれています。以下のテストで構成されています: + +- ユニットテスト:`test/unit/` +- 統合テスト:`test/integration/` +- ファジングテスト:`test/fuzz/` + +テストに関する詳細な情報は`test/readme.md`に記載されています。テストの詳細については、そちらを参照してください。 + +## 始め方 + +1. リポジトリをクローンする + +```shell +git clone https://github.com/codefox-inc/dao-community-contracts.git +``` + +2. 依存関係を更新する + +```shell +foundryup +``` + +3. 依存関係をインストールする + +```shell +make install +``` + +4. 以下のコマンドでコントラクトをコンパイルします + +```shell +forge compile +``` + +5. 以下のコマンドでテストを実行します + +```shell +make test +``` + +7. 以下のコマンドでカバレッジを実行します + +```shell +make coverage +``` + +## 参考資料 + +数学的公式と表 +これらは、投票力 <-> バーントークン計算のための交換関数とテストで使用した値です。 + +``` + +x = (2 * sqrt(306.25 + 30y) - 5) / 30 - 1 +y = (15*x^2+35\*x)/2 + +x: 発行されたトークン +y: バーンされたトークン + +``` + +| Minted Token (lvl) | Level | Burned Token | +| ------------------ | ----- | ------------ | +| 0 | 1 | 0 | +| 1 | 2 | 25 | +| 2 | 3 | 65 | +| 3 | 4 | 120 | +| 4 | 5 | 190 | +| 5 | 6 | 275 | +| 6 | 7 | 375 | +| 7 | 8 | 490 | +| 8 | 9 | 620 | +| 9 | 10 | 765 | +| 10 | 11 | 925 | +| 11 | 12 | 1100 | +| 12 | 13 | 1290 | +| 13 | 14 | 1495 | +| 14 | 15 | 1715 | +| 15 | 16 | 1950 | +| 16 | 17 | 2200 | +| 17 | 18 | 2465 | +| 18 | 19 | 2745 | +| 19 | 20 | 3040 | +| 20 | 21 | 3350 | +| 21 | 22 | 3675 | +| 22 | 23 | 4015 | +| 23 | 24 | 4370 | +| 24 | 25 | 4740 | +| 25 | 26 | 5125 | +| 26 | 27 | 5525 | +| 27 | 28 | 5940 | +| 28 | 29 | 6370 | +| 29 | 30 | 6815 | +| 30 | 31 | 7275 | +| 31 | 32 | 7750 | +| 32 | 33 | 8240 | +| 33 | 34 | 8745 | +| 34 | 35 | 9265 | +| 35 | 36 | 9800 | +| 36 | 37 | 10350 | +| 37 | 38 | 10915 | +| 38 | 39 | 11495 | +| 39 | 40 | 12090 | +| 40 | 41 | 12700 | +| 41 | 42 | 13325 | +| 42 | 43 | 13965 | +| 43 | 44 | 14620 | +| 44 | 45 | 15290 | +| 45 | 46 | 15975 | +| 46 | 47 | 16675 | +| 47 | 48 | 17390 | +| 48 | 49 | 18120 | +| 49 | 50 | 18865 | +| 50 | 51 | 19625 | +| 51 | 52 | 20400 | +| 52 | 53 | 21190 | +| 53 | 54 | 21995 | +| 54 | 55 | 22815 | +| 55 | 56 | 23650 | +| 56 | 57 | 24500 | +| 57 | 58 | 25365 | +| 58 | 59 | 26245 | +| 59 | 60 | 27140 | +| 60 | 61 | 28050 | +| 61 | 62 | 28975 | +| 62 | 63 | 29915 | +| 63 | 64 | 30870 | +| 64 | 65 | 31840 | +| 65 | 66 | 32825 | +| 66 | 67 | 33825 | +| 67 | 68 | 34840 | +| 68 | 69 | 35870 | +| 69 | 70 | 36915 | +| 70 | 71 | 37975 | +| 71 | 72 | 39050 | +| 72 | 73 | 40140 | +| 73 | 74 | 41245 | +| 74 | 75 | 42365 | +| 75 | 76 | 43500 | +| 76 | 77 | 44650 | +| 77 | 78 | 45815 | +| 78 | 79 | 46995 | +| 79 | 80 | 48190 | +| 80 | 81 | 49400 | +| 81 | 82 | 50625 | +| 82 | 83 | 51865 | +| 83 | 84 | 53120 | +| 84 | 85 | 54390 | +| 85 | 86 | 55675 | +| 86 | 87 | 56975 | +| 87 | 88 | 58290 | +| 88 | 89 | 59620 | +| 89 | 90 | 60965 | +| 90 | 91 | 62325 | +| 91 | 92 | 63700 | +| 92 | 93 | 65090 | +| 93 | 94 | 66495 | +| 94 | 95 | 67915 | +| 95 | 96 | 69350 | +| 96 | 97 | 70800 | +| 97 | 98 | 72265 | +| 98 | 99 | 73745 | +| 99 | 100 | 75240 | +| 100 | 101 | 76750 | +| 101 | 102 | 78275 | +| 102 | 103 | 79815 | +| 103 | 104 | 81370 | +| 104 | 105 | 82940 | +| 105 | 106 | 84525 | +| 106 | 107 | 86125 | +| 107 | 108 | 87740 | +| 108 | 109 | 89370 | +| 109 | 110 | 91015 | +| 110 | 111 | 92675 | + +.... + +### Documents + +- [Foundry Book](https://book.getfoundry.sh/) +- [OpenZeppelin](https://docs.openzeppelin.com/) + +``` + +``` diff --git a/script/DeployContracts.s.sol b/script/DeployContracts.s.sol index 7ca17b0..2128202 100644 --- a/script/DeployContracts.s.sol +++ b/script/DeployContracts.s.sol @@ -145,4 +145,64 @@ contract DeployContracts is Script { participant: DEFAULT_ANVIL_KEY2 }); } + + + // deploy the contracts on the base sepolia network for testing + function deploymentsOnBaseSepilia() public returns (DeploymentResult memory) { + admin = vm.addr(vm.envUint("PRIVATE_KEY_ADMIN")); + pauser = vm.addr(vm.envUint("PRIVATE_KEY_ADMIN")); + minter = makeAddr("minter"); + burner = makeAddr("burner"); + manager = makeAddr("manager"); + exchanger = makeAddr("exchanger"); + + // deploy the utility token using the OpenZeppelin Upgrades library + // address proxy = Upgrades.deployUUPSProxy( + // "ERC20UpgradeableTokenV1.sol", + // abi.encodeCall( + // ERC20UpgradeableTokenV1.initialize, ("AMA coin", "AMA", admin, pauser, minter, burner, admin) + // ) + // ); + + /// This is using the UnsafeUpgrades method to deploy the UUPS in test environment not in production. This can be run to get the test coverage. + address implementation = address(new ERC20UpgradeableTokenV1()); + address proxy = UnsafeUpgrades.deployUUPSProxy( + implementation, + abi.encodeCall( + ERC20UpgradeableTokenV1.initialize, ("AMA coin", "AMA", admin, pauser, minter, burner, admin) + ) + ); + + utilityToken = ERC20UpgradeableTokenV1(proxy); + + // deploy the gov token + govToken = new GovToken("Governance Token", "GOV", admin, minter, burner, exchanger); + + // deploy the voting power exchange + votingPowerExchange = + new VotingPowerExchange(address(govToken), address(utilityToken), admin, manager, exchanger); + + vm.startPrank(admin); + // give exchange the minter role of govToken + govToken.grantRole(govToken.MINTER_ROLE(), address(votingPowerExchange)); + // give exchange the burner role of utilityToken + utilityToken.grantRole(utilityToken.BURNER_ROLE(), address(votingPowerExchange)); + // give exchange the voting power exchange role of govToken + govToken.grantRole(govToken.VOTING_POWER_EXCHANGE_ROLE(), address(votingPowerExchange)); + vm.stopPrank(); + + return DeploymentResult({ + utilityToken: address(utilityToken), + govToken: address(govToken), + exchange: address(votingPowerExchange), + admin: admin, + pauser: pauser, + minter: minter, + burner: burner, + manager: manager, + exchanger: exchanger, + deployerKey: DEFAULT_ANVIL_KEY, + participant: DEFAULT_ANVIL_KEY2 + }); + } }