# [Bunzz Decipher] Nounsの『NounsDAOLogicV1』コントラクトを理解しよう！

> NounsDAOLogicV1というコントラクトは、Nouns DAO（分散型自治組織）システムの重要な要素です。

- URL: https://cardene.dev/articles/68fcb8c7554d49
- 公開日: 2023-09-04
- 更新日: 2023-09-04
- トピック: Blockchain

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回はBunzzの新機能『**DeCipher**』を使用して、Nounsの「**NounsDAOLogicV1**」コントラクトを見てみようと思います。

『**DeCipher**』はAIを使用してコントラクトのドキュメントを自動生成してくれるサービスです。

https://www.bunzz.dev/decipher

詳しい使い方に関しては以下の記事を参考にしてください！

https://zenn.dev/heku/articles/33266f0c19d523

:::message
この記事はあくまで個人が「DeCipher」を使用してみての記事になります。
間違った部分などもあるかと思いますが、その際はコメントなどで教えてください。
:::

今回使用する『**DeCipher**』のリンクは以下になります。

https://app.bunzz.dev/decipher/chains/1/addresses/0xa43aFE317985726E4e194eb061Af77fbCb43F944

Etherscanのリンクは以下になります。

https://etherscan.io/address/0xa43aFE317985726E4e194eb061Af77fbCb43F944

# 概要

**NounsDAOLogicV1**というコントラクトは、Nouns DAO（分散型自治組織）システムの重要な要素です。

## 目的
**Nouns DAO**のルールとロジックを管理することがこのコントラクトの目的です。

このコントラクトは、**Nouns DAO**内の提案、投票期間、投票遅延、閾値などのロジックを管理します。
具体的には、提案がどのように行われ、いつ投票が行われ、どの割合の投票が提案の承認に必要かなどのパラメータを設定します。

## 責務
コントラクトの責務としては、以下の役割があります。

### 提案の閾値設定
提案を有効と見なすために必要な最小および最大の投票割合を設定します。
最小は`0.01％`、最大は`10％`です。

### 投票期間の設定
DAOメンバーが投票を行う期間を設定します。
最短は約24時間、最長は約2週間です。

### 投票遅延の設定
提案後から投票が開始されるまでの期間を設定します。
最短は1日、最長は約1週間です。

### 提案の管理
最新の提案IDを管理し、DAO内の提案を追跡・管理します。

### コントラクトの管理
コントラクト自体の管理、現在の管理者や保留中の管理者を管理します。

# 使い方

**NounsDAOLogicV1**コントラクトは、Nouns DAO（分散型自治組織）システムの一部であり、DAO内の提案と投票を管理するために使われます。

## 1. 初期化
契約を使用するには、まずはじめにinitialize関数を呼び出して初期化を行います。この関数は、DAOの最初の設定を行います。具体的には、タイムロックアドレス、Nounsトークンアドレス、拒否者のアドレス、投票期間、投票遅延、提案閾値、クオラム投票などが設定されます。

## 2. 提案の作成
提案を作成するには、`ProposalCreated`イベントを呼び出します。
このイベントには、提案のID、提案者のアドレス、提案の対象、値、署名、コールデータ、開始ブロック、終了ブロック、提案の説明などが必要です。

また、特定の要件を持つ提案を作成する場合は、`ProposalCreatedWithRequirements`イベントを使用できます。
このイベントも同じパラメータを必要としますが、提案閾値とクオラム投票も含まれます。

## 3. 提案への投票
提案が作成されると、DAOメンバーはその提案に投票できます。
投票期間はコントラクトの初期化時に設定され、`MIN_VOTING_PERIOD`から`MAX_VOTING_PERIOD`の範囲内で設定します。
投票遅延も初期化時に設定され、`MIN_VOTING_DELAY`から`MAX_VOTING_DELAY`の範囲内で設定します。

## 4. 提案閾値の設定
提案閾値は、提案が有効とされるために必要な最小な投票数です。
この値は初期化時に設定され、`MIN_PROPOSAL_THRESHOLD_BPS`から`MAX_PROPOSAL_THRESHOLD_BPS`の範囲内で設定します。

## 5. クオラム投票の設定
クオラム投票で使用される、提案が承認されるために必要な最小な投票数です。
この値は初期化時に設定されます。

## 6. 最新の提案の確認
`latestProposalIds`関数を呼び出すことで、最新の提案のIDを確認できます。

# パラメーター

なし。

# コントラクト

## NounsDAOEvents

### ProposalCreated

:::details ProposalCreated
```solidity
event ProposalCreated(
    uint256 id,
    address proposer,
    address[] targets,
    uint256[] values,
    string[] signatures,
    bytes[] calldatas,
    uint256 startBlock,
    uint256 endBlock,
    string description
);
```
:::

**概要**
新しい提案が作成されたときに発行されるイベント。

**詳細**
- `id`
	- 提案のIDを示す数値。
- `proposer`
	- 提案を行ったアドレス。
- `targets`
	- 提案の対象となるコントラクトアドレスの配列。
- `values`
	- 各対象への値の配列。
- `signatures`
	- 各対象の関数署名の文字列の配列。
- `calldatas`
	- 各対象の関数呼び出しデータのバイト配列の配列。
- `startBlock`
	- 提案が開始されるブロック番号。
- `endBlock`
	- 提案が終了するブロック番号。
- `description`
	- 提案の説明を示す文字列。

---

### ProposalCreatedWithRequirements

:::details ProposalCreatedWithRequirements
```solidity
event ProposalCreatedWithRequirements(
    uint256 id,
    address proposer,
    address[] targets,
    uint256[] values,
    string[] signatures,
    bytes[] calldatas,
    uint256 startBlock,
    uint256 endBlock,
    uint256 proposalThreshold,
    uint256 quorumVotes,
    string description
);
```
:::

**概要**
特定の要件を持つ新しい提案が作成されたときに発行されるイベント。

**詳細**
- `id`
	- 提案のIDを示す数値。
- `proposer`
	- 提案を行ったアドレス。
- `targets`
	- 提案の対象となるコントラクトアドレスの配列。
- `values`
	- 各対象への値の配列。
- `signatures`
	- 各対象の関数署名の文字列の配列。
- `calldatas`
	- 各対象の関数呼び出しデータのバイト配列の配列。
- `startBlock`
	- 提案が開始されるブロック番号。
- `endBlock`
	- 提案が終了するブロック番号。
- `proposalThreshold`
	- 提案の閾値を示す数値。
- `quorumVotes`
	- クオラム投票の数を示す数値。
- `description`
	- 提案の説明を示す文字列。

---

### VoteCast
```solidity
event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 votes, string reason);
```
**概要**
投票が提案に対して行われた時に発行されるイベント。

**パラメータ**
- `voter`
	- 投票者のアドレス。
- `proposalId`
	- 投票された提案のID。
- `support`
	- 投票の支持値（0=反対、1=賛成、2=棄権）。
- `votes`
	- 投票者が投票した票数。
- `reason`
	-投票者が提供した投票理由。

---

### ProposalCanceled
```solidity
event ProposalCanceled(uint256 id);
```
**概要**
提案がキャンセルされた時に発行されるイベント。

**パラメータ**
- `id`
	- キャンセルされた提案のID。

---

### ProposalQueued
```solidity
event ProposalQueued(uint256 id, uint256 eta);
```
**概要**
提案が**NounsDAOExecutor**にキューイングされた時に発行されるイベント。

**パラメータ**
- `id`
	- キューイングされた提案のID。
- `eta`
	- キューイングされた日時（ETA）。

---


### ProposalExecuted

:::details ProposalExecuted
```solidity
event ProposalExecuted(uint256 id);
```
:::

**概要**
**NounsDAOExecutor**で提案が実行された時に発行されるイベント。

**パラメータ**
- `id`
	- 実行された提案の識別子。

---

### ProposalVetoed

:::details ProposalVetoed
```solidity
event ProposalVetoed(uint256 id);
```
:::

**概要**
提案が**vetoAddress**によって拒否された時に発行されるイベント。

**パラメータ**
- `id`
	- 拒否された提案の識別子。

---

### VotingDelaySet

:::details VotingDelaySet
```solidity
event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay);
```
:::

**概要**
投票の遅延が変更された時に発行されるイベント。

**パラメータ**
- `oldVotingDelay`
	- 変更前の投票遅延時間。
- `newVotingDelay`
	- 変更後の投票遅延時間。

---

### VotingPeriodSet

:::details VotingPeriodSet
```solidity
event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod);
```
:::

**概要**
投票期間が変更された時に発行されるイベント。

**パラメータ**
- `oldVotingPeriod`
	- 変更前の投票期間。
- `newVotingPeriod`
	- 変更後の投票期間。

---

### NewImplementation

:::details NewImplementation
```solidity
event NewImplementation(address oldImplementation, address newImplementation);
```
:::

**概要**
実装アドレスが変更された時に発行されるイベント。

**パラメータ**
- `oldImplementation` 
	- 変更前の実装アドレス。
- `newImplementation`
	- 新しい実装アドレス。

---

### ProposalThresholdBPSSet

:::details ProposalThresholdBPSSet
```solidity
event ProposalThresholdBPSSet(uint256 oldProposalThresholdBPS, uint256 newProposalThresholdBPS);
```
:::

**概要**
提案の閾値（基準点数）が設定された時に発行されるイベント。

**パラメータ**
- `oldProposalThresholdBPS`
	- 変更前の提案閾値（基準点数）。
- `newProposalThresholdBPS`
	- 変更後の提案閾値（基準点数）。

---

### QuorumVotesBPSSet

:::details QuorumVotesBPSSet
```solidity
event QuorumVotesBPSSet(uint256 oldQuorumVotesBPS, uint256 newQuorumVotesBPS);
```
:::

**概要**
議決権の閾値（基準点数）が設定された時に発行されるイベント。

**パラメータ**
- `oldQuorumVotesBPS`
	- 変更前の議決権閾値（基準点数）。
- `newQuorumVotesBPS` 
	- 変更後の議決権閾値（基準点数）。

---

### NewPendingAdmin

:::details NewPendingAdmin
```solidity
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
```
:::

**概要**
保留中の管理者アドレスが変更された時に発行されるイベント。

**パラメータ**
- `oldPendingAdmin`
	- 変更前の保留中の管理者アドレス。
- `newPendingAdmin`
	- 新しい保留中の管理者アドレス。

---

### NewAdmin

:::details NewAdmin
```solidity
event NewAdmin(address oldAdmin, address newAdmin);
```
:::

**概要**
管理者アドレスが変更された時に発行されるイベント。

**パラメータ**
- `oldAdmin`
	- 変更前の管理者アドレス。
- `newAdmin`
	- 新しい管理者アドレス。

---

### NewVetoer

:::details NewVetoer
```solidity
event NewVetoer(address oldVetoer, address newVetoer);
```
:::

**概要**
`vetoer`アドレスが変更された時に発行されるイベント。

**パラメータ**
- `oldVetoer`
	- 変更前の`vetoer`アドレス。
- `newVetoer`
	- 新しい`vetoer`アドレス。

---

## NounsDAOProxyStorage

### admin

:::details admin
```solidity
address public admin;
```
:::

**概要**
スマートコントラクトの管理者のアドレスを示します。
管理者はコントラクトの操作や変更を行う権限を持ちます。

**詳細**
管理者はコントラクト内の重要な操作を実行するための権限を持ちます。
例えば、コントラクトのアップグレードや設定変更などの操作が含まれます。
この変数の値を変更することで、新しいアドレスに管理者権限を移譲することができます。

---

### pendingAdmin

:::details pendingAdmin
```solidity
address public pendingAdmin;
```
:::

**概要**
保留中の管理者アドレスを示します。
新しい管理者が指定されて、現在の管理者が承認するまでの間に保持されるアドレスです。

**詳細**
`pendingAdmin`変数は新しい管理者が指定された場合に、そのアドレスを一時的に保持します。
現在の管理者が新しい管理者を承認するまで、実際の管理者権限は移行しません。
この仕組みによって、新しい管理者に対する変更の承認プロセスが確立されています。

---

### implementation

:::details implementation
```solidity
address public implementation;
```
:::

**概要**
ガバナーのアクティブなブレイン（実装）のアドレスを示します。
ガバナーの主要な機能が実際に実装されているコントラクトのアドレスです。

**詳細**
`implementation`変数はガバナーの機能が実装されているコントラクトのアドレスを保持します。
このアドレスは、ガバナーの実際の機能やロジックが存在する場所を指します。
新しい機能の追加やアップグレードの際に、このアドレスが変更されることがあります。

---

## NounsDAOStorageV1

### vetoer

:::details vetoer
```solidity
address public vetoer;
```
:::

**概要**
任意の提案を拒否する権限を持つ**Vetoer**（拒否者）のアドレスを格納する変数。

**詳細**
**Vetoer**は、提案が行われた時にその提案を拒否する権限を持ちます。
提案が投票される前でも、**Vetoer**は提案を拒否することができます。
これにより、一定の要件を満たさない提案を防ぐことができます。

---

### votingDelay

:::details votingDelay
```solidity
uint256 public votingDelay;
```
:::

**概要**
`提案された後に投票が行われるまでの遅延期間をブロック数で示す変数。

**詳細**
遅延期間はブロック数で表され、一定の期間が経過した後に投票が始まる仕組みを提供します。
遅延期間の目的は、コミュニティメンバーが提案内容を十分に検討し、意思決定を行うために時間を確保することです。

---

### votingPeriod

:::details votingPeriod
```solidity
uint256 public votingPeriod;
```
:::

**概要**
提案に対する投票期間の長さをブロック数で示す変数。

**詳細**
投票期間は提案が提出されてから一定の期間が経過した後に終了し、その間にコミュニティメンバーは支持、反対、棄権などの投票を行います。
投票期間の適切な設定は、意思決定プロセスの効率と信頼性に影響を与えます。

---

### proposalThresholdBPS

:::details proposalThresholdBPS
```solidity
uint256 public proposalThresholdBPS;
```
:::

**概要**
投票者が提案者になるために必要な基準となる、投票数の基準を示す基準点数（Basis Point数）を示す変数。

**詳細**
基準点数はBasis Point数（`1`ベーシスポイントは`0.01％`を表します）で表され、一定の投票閾値を満たすことが必要な投票数を計算する際に使用されます。
この基準点数はプロトコルの設計において、提案者としての資格を持つための要件を決定するために使用されます。
ただし、`GovernerBravo`とは異なる値となることに注意してください。

:::message
**GovernerBravo**
DeFi（分散型金融）プロトコルの一部であるCompoundプロトコルにおいて使用される、提案と投票に関する仕組みを指します。
Compoundプロトコルは、イーサリアムベースのスマートコントラクト上で運用される金融プラットフォームであり、ユーザーは資産を供給して利子を受け取ることや、借り入れを行うことができます。

GovernerBravoは、Compoundプロトコル内で行われる提案や投票を管理するためのスマートコントラクトです。
このスマートコントラクトは、コミュニティメンバーが新しいプロトコルの機能追加や変更を提案し、それに対して投票を行うためのプラットフォームを提供します。
投票結果に基づいて提案が可決または否決され、プロトコルの運営方針が決定される仕組みです。

GovernerBravoは、提案の投票閾値、クオーラム要件、投票期間などのパラメータを設定できるため、プロトコルの運営においてコミュニティメンバーの意見を反映させることができる重要なツールです。提案と投票のプロセスを効率的に管理し、プロトコルの進化や変更に対して民主的な意思決定を行うために活用されています。
:::

---

### quorumVotesBPS

:::details quorumVotesBPS
```solidity
uint256 public quorumVotesBPS;
```
:::

**概要**
提案の可決に必要なクオーラム達成に必要な基準点数（Basis Point数）を示す変数。

**詳細**
クオーラム達成の要件を満たすためには、一定の投票閾値を超える投票が集まる必要があります。
この基準点数はBasis Point数で表され、プロトコルの運営と提案の信頼性を確保するために使用されます。
ただし、`GovernerBravo`とは異なる値となることに注意してください。

**パラメータ**
- なし

---

### proposalCount

:::details proposalCount
```solidity
uint256 public proposalCount;
```
:::

**概要**
提案の総数を示す変数。

---

### timelock

:::details timelock
```solidity
INounsDAOExecutor public timelock;
```
:::

**概要**
**Nouns DAO Executor（タイムロック）** のアドレスを示す変数。

**詳細**
タイムロックは、一定の期間が経過した後に実行されるトランザクションをスケジュールする機能を提供します。
提案が可決された後に実行されるトランザクションを遅延させるために使用されることがあります。

---

### nouns

:::details nouns
```solidity
NounsTokenLike public nouns;
```
:::

**概要**
**Nouns**トークンのアドレスを示す変数。

**詳細**
**Nouns**トークンは、プロトコル内で使用されるトークンであり、投票やプロトコルの運営に関連する機能に使用されることがあります。


---

### proposals

:::details proposals
```solidity
mapping(uint256 => Proposal) public proposals;
```
:::

**概要**
すべての提案の詳細情報を提供するマッピング配列。

**詳細**
各提案の情報は、`Proposal`構造体に格納されており、提案の作成者、投票期間、対象アドレスなどの情報が含まれています。

**パラメータ**
- `proposalId`
	- 提案のID。

---

### latestProposalIds

:::details latestProposalIds
```solidity
mapping(address => uint256) public latestProposalIds;
```
:::

**概要**
各提案者の最新の提案のIDを保持するマッピング配列。

**詳細**
提案者が新しい提案を行った場合、この変数を更新して最新の提案のIDを関連付けます。

**パラメータ**
- `proposer`
	- 提案者のアドレス。

---

### Proposal構造体

:::details Proposal構造体
```solidity
struct Proposal {
    uint256 id;
    address proposer;
    uint256 proposalThreshold;
    uint256 quorumVotes;
    uint256 eta;
    address[] targets;
    uint256[] values;
    string[] signatures;
    bytes[] calldatas;
    uint256 startBlock;
    uint256 endBlock;
    uint256 forVotes;
    uint256 againstVotes;
    uint256 abstainVotes;
    bool canceled;
    bool vetoed;
    bool executed;
    mapping(address => Receipt) receipts;
}
```
:::

**概要**
提案の詳細情報を格納する構造体。

**パラメータ**
- `uint256 id`
	- 提案の一意のID。
- `address proposer`
	- 提案者のアドレス。
- `uint256 proposalThreshold`
	- 提案作成時に必要な投票数の閾値。
- `uint256 quorumVotes`
	- クオーラム達成のための必要な投票数。
- `uint256 eta`
	- 提案の投票が成功した場合の実行予定時刻。
- `address[] targets`
	- 呼び出し対象アドレスのリスト。
- `uint256[] values`
	- 呼び出し時の値（msg.value）のリスト。
- `string[] signatures`
	- 呼び出す関数のシグネチャのリスト。
- `bytes[] calldatas`
	- 各呼び出しのためのcalldataのリスト。
- `uint256 startBlock`
	- 投票開始ブロック番号。
- `uint256 endBlock`
	- 投票終了ブロック番号。
- `uint256 forVotes`
	- 賛成票の数。
- `uint256 againstVotes`
	- 反対票の数。
- `uint256 abstainVotes`
	- 棄権票の数。
- `bool canceled`
	- 提案がキャンセルされたかのフラグ。
- `bool vetoed`
	- 提案が拒否されたかのフラグ。
- `bool executed`
	- 提案が実行されたかのフラグ。
- `mapping(address => Receipt) receipts`
	- 各投票者の投票情報のマッピング。

---

### Receipt

:::details Receipt
```solidity
struct Receipt {
    /// @notice 投票が行われたかどうか
    bool hasVoted;
    /// @notice 投票者が提案を支持するか棄権するか
    uint8 support;
    /// @notice 投票者が行った投票数
    uint96 votes;
}
```
:::

**概要**
投票者が特定の提案に関して投票した時の情報を保持する構造体。

**パラメータ**
- `hasVoted`
	- 投票が行われたかどうかを示すブール値。
	- `true`の場合、投票が行われたことを示します。
- `support`
	- 投票者が提案を支持するか、または棄権するかを示す数値です。
	- `0`は棄権、`1`は支持、その他の値はその他の意味を持つ可能性があります。
- `votes`
	- 投票者が行った投票数を示す整数。
	- 投票者が複数の投票を行った場合、その合計が格納されます。

---

### ProposalState

:::details ProposalState
```solidity
enum ProposalState {
    Pending,
    Active,
    Canceled,
    Defeated,
    Succeeded,
    Queued,
    Expired,
    Executed,
    Vetoed
}
```
:::

**概要**
提案が状態を示す列挙型。

**パラメータ**
- `Pending`
	- 提案が保留中の状態。
	- まだ処理されていないことを示します。
- `Active`
	- 提案がアクティブな状態。
	- 処理中であり、投票や実行が進行中であることを示します。
- `Canceled`
	- 提案が取り消された状態。
	- 提案が無効となり、処理が中断されたことを示します。
- `Defeated`
	- 提案が否決された状態。
	- 投票結果が否定的であることを示します。
- `Succeeded`
	- 提案が成功した状態。
	- 投票が肯定的な結果となり、処理が進行することを示します。
- `Queued`
	- 提案がキューに入った状態。
	- 処理が待機中であることを示します。
- `Expired`
	- 提案が期限切れの状態。
	- 提案の有効期限が切れたことを示します。
- `Executed`
	- 提案が実行された状態。
	- 提案が正常に実行されたことを示します。
- `Vetoed`
	- 提案が拒否された状態。
	- 特定の権限により提案が却下されたことを示します。

---

## NounsDAOLogicV1

### name

:::details name
```solidity
string public constant name = 'Nouns DAO';
```
:::

**概要**
コントラクトの名前。

### MIN_PROPOSAL_THRESHOLD_BPS

:::details MIN_PROPOSAL_THRESHOLD_BPS
```solidity
uint256 public constant MIN_PROPOSAL_THRESHOLD_BPS = 1;
```
:::

**概要**
最小の提案閾値。

**詳細**
提案の閾値を基準ポイント（basis points、BPS）で表しており、`0.01%`を表します。
提案が受け入れられるために必要な最小のステーキング額を示します。

### MAX_PROPOSAL_THRESHOLD_BPS

:::details MAX_PROPOSAL_THRESHOLD_BPS
```solidity
uint256 public constant MAX_PROPOSAL_THRESHOLD_BPS = 1_000;
```
:::

**概要**
最大の提案閾値。

**詳細**
提案の閾値を基準ポイント（basis points、BPS）で表しており、`10%`を表します。
提案が受け入れられるために必要な最大のステーキング額を示します。

### MIN_VOTING_PERIOD

:::details MIN_VOTING_PERIOD
```solidity
uint256 public constant MIN_VOTING_PERIOD = 5_760;
```
:::

**概要**
最小の投票期間。

**詳細**
投票期間をブロック数で表しており、約24時間を表します。
提案の投票が行われる期間を示します。

### MAX_VOTING_PERIOD

:::details MAX_VOTING_PERIOD
```solidity
uint256 public constant MAX_VOTING_PERIOD = 80_640;
```
:::

**概要**
最大の投票期間。

**詳細**
投票期間をブロック数で表しており、約2週間を表します。
提案の投票が行われる最大の期間を示します。

### MIN_VOTING_DELAY

:::details MIN_VOTING_DELAY
```solidity
uint256 public constant MIN_VOTING_DELAY = 1;
```
:::

**概要**
最小の投票遅延。

**詳細**
提案が作成されてから実際の投票が始まるまでの最小の待機期間をブロック数で示します。

### MAX_VOTING_DELAY

:::details MAX_VOTING_DELAY
```solidity
uint256 public constant MAX_VOTING_DELAY = 40_320;
```
:::

**概要**
最大の投票遅延。

**詳細**
提案が作成されてから実際の投票が始まるまでの最大の待機期間をブロック数で示します。
約1週間を表します。

### MIN_QUORUM_VOTES_BPS

:::details MIN_QUORUM_VOTES_BPS
```solidity
uint256 public constant MIN_QUORUM_VOTES_BPS = 200;
```
:::

**概要**
最小の議決権行使基準。

**詳細**
議決権の行使基準を基準ポイント（basis points、BPS）で表しており、`2%`を表します。
提案が受け入れられるために必要な最小の議決権行使割合を示します。

### MAX_QUORUM_VOTES_BPS

:::details MAX_QUORUM_VOTES_BPS
```solidity
uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000;
```
:::

**概要**
最大の議決権行使基準。

**詳細**
`議決権の行使基準を基準ポイント（basis points、BPS）で表しており、`20%`を表します。
提案が受け入れられるために必要な最大の議決権行使割合を示します。

### proposalMaxOperations

:::details proposalMaxOperations
```solidity
uint256 public constant proposalMaxOperations = 10;
```
:::

**概要**
1つの提案に含まれる最大のアクション数。

**詳細**
1つの提案に含まれることができる最大のアクション数を示します。
提案内で実行できる操作の数が制限されています。

### DOMAIN_TYPEHASH

:::details DOMAIN_TYPEHASH
```solidity
bytes32 public constant DOMAIN_TYPEHASH = keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');
```
:::

**概要**
コントラクトのドメインの **EIP712**ハッシュ。

**詳細**
バイト列型の定数として宣言されており、**EIP712**ドメインの型ハッシュを格納しています。
このハッシュは、**EIP712**メッセージの署名と検証に使用されます。

### BALLOT_TYPEHASH

:::details BALLOT_TYPEHASH
```solidity
bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');
```
:::

**概要**
コントラクトで使用されるバラット（投票）の**EIP712**ハッシュ。

**詳細**
投票メッセージの署名と検証に使用されます。

---

### initialize

:::details initialize
```solidity
function initialize(
    address timelock_,
    address nouns_,
    address vetoer_,
    uint256 votingPeriod_,
    uint256 votingDelay_,
    uint256 proposalThresholdBPS_,
    uint256 quorumVotesBPS_
) public virtual {
    // 初期化済みでないことを確認
    require(address(timelock) == address(0), 'NounsDAO::initialize: 既に初期化済み');
    // 管理者のみ実行可能であることを確認
    require(msg.sender == admin, 'NounsDAO::initialize: 管理者のみ実行可能');
    // 無効なtimelockアドレスを排除
    require(timelock_ != address(0), 'NounsDAO::initialize: 無効なtimelockアドレス');
    // 無効なnounsアドレスを排除
    require(nouns_ != address(0), 'NounsDAO::initialize: 無効なnounsアドレス');
    // 有効な投票期間であることを確認
    require(
        votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD,
        'NounsDAO::initialize: 無効な投票期間'
    );
    // 有効な投票遅延時間であることを確認
    require(
        votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY,
        'NounsDAO::initialize: 無効な投票遅延時間'
    );
    // 有効な提案閾値（基準点）であることを確認
    require(
        proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS,
        'NounsDAO::initialize: 無効な提案閾値'
    );
    // 有効なクオーラム投票閾値（基準点）であることを確認
    require(
        quorumVotesBPS_ >= MIN_QUORUM_VOTES_BPS && quorumVotesBPS_ <= MAX_QUORUM_VOTES_BPS,
        'NounsDAO::initialize: 無効なクオーラム投票閾値'
    );

    // イベント発行: 投票期間が設定されたことを示す
    emit VotingPeriodSet(votingPeriod, votingPeriod_);
    // イベント発行: 投票遅延が設定されたことを示す
    emit VotingDelaySet(votingDelay, votingDelay_);
    // イベント発行: 提案閾値が設定されたことを示す
    emit ProposalThresholdBPSSet(proposalThresholdBPS, proposalThresholdBPS_);
    // イベント発行: クオーラム投票閾値が設定されたことを示す
    emit QuorumVotesBPSSet(quorumVotesBPS, quorumVotesBPS_);

    // timelockアドレスを設定
    timelock = INounsDAOExecutor(timelock_);
    // nounsアドレスを設定
    nouns = NounsTokenLike(nouns_);
    // vetoerアドレスを設定
    vetoer = vetoer_;
    // 投票期間を設定
    votingPeriod = votingPeriod_;
    // 投票遅延時間を設定
    votingDelay = votingDelay_;
    // 提案閾値を設定
    proposalThresholdBPS = proposalThresholdBPS_;
    // クオーラム投票閾値を設定
    quorumVotesBPS = quorumVotesBPS_;
}
```
:::

**概要**
コントラクトの初期化を行う関数。
初期化は1回しか行えず、管理者のみが実行できます。

**詳細**
- 初期化がまだ行われていないことを確認します。
- 実行者が管理者であることを確認します。
- 提供されたアドレスが無効でないことを確認します。
- 投票期間、投票遅延時間、提案閾値、クオーラム投票閾値が有効な範囲内にあることを確認します。
- イベントを発行して新しい設定が確認されるようにします。

**引数**
- `timelock_`
	- **NounsDAOExecutor**のアドレス。
- `nouns_`
	- **NOUN**トークンのアドレス。
- `vetoer_`
	- 単独で提案を拒否できるアドレス。
- `votingPeriod_`
	- 初期投票期間。
- `votingDelay_`
	- 初期投票遅延時間。
- `proposalThresholdBPS_`
	- 初期提案閾値（基準点）。
- `quorumVotesBPS_`
	- 初期クオーラム投票閾値（基準点）。

**戻り値**
この関数は戻り値を持ちません。

---

### ProposalTemp

:::details ProposalTemp
```solidity
struct ProposalTemp {
    uint256 totalSupply;
    uint256 proposalThreshold;
    uint256 latestProposalId;
    uint256 startBlock;
    uint256 endBlock;
}
```
:::

**概要**
プロポーザルの一時的な情報を格納する構造体。
プロポーザルの進行状況や必要な情報を保持します。

**パラメータ**
- `totalSupply`
	- 現在のトークンの総供給量。
- `proposalThreshold`
	- プロポーザルが受け入れられるための閾値。
- `latestProposalId`
	- 最新のプロポーザルID。
- `startBlock`
	- プロポーザルの開始ブロック番号。
- `endBlock`
	- プロポーザルの終了ブロック番号。

---

### propose

:::details propose
```solidity
function propose(
        address[] memory targets,
        uint256[] memory values,
        string[] memory signatures,
        bytes[] memory calldatas,
        string memory description
    ) public returns (uint256) {
        ProposalTemp memory temp;

        temp.totalSupply = nouns.totalSupply();

        temp.proposalThreshold = bps2Uint(proposalThresholdBPS, temp.totalSupply);

        require(
            nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold,
            'NounsDAO::propose: proposer votes below proposal threshold'
        );
        require(
            targets.length == values.length &&
                targets.length == signatures.length &&
                targets.length == calldatas.length,
            'NounsDAO::propose: proposal function information arity mismatch'
        );
        require(targets.length != 0, 'NounsDAO::propose: must provide actions');
        require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions');

        temp.latestProposalId = latestProposalIds[msg.sender];
        if (temp.latestProposalId != 0) {
            ProposalState proposersLatestProposalState = state(temp.latestProposalId);
            require(
                proposersLatestProposalState != ProposalState.Active,
                'NounsDAO::propose: one live proposal per proposer, found an already active proposal'
            );
            require(
                proposersLatestProposalState != ProposalState.Pending,
                'NounsDAO::propose: one live proposal per proposer, found an already pending proposal'
            );
        }

        temp.startBlock = block.number + votingDelay;
        temp.endBlock = temp.startBlock + votingPeriod;

        proposalCount++;
        Proposal storage newProposal = proposals[proposalCount];

        newProposal.id = proposalCount;
        newProposal.proposer = msg.sender;
        newProposal.proposalThreshold = temp.proposalThreshold;
        newProposal.quorumVotes = bps2Uint(quorumVotesBPS, temp.totalSupply);
        newProposal.eta = 0;
        newProposal.targets = targets;
        newProposal.values = values;
        newProposal.signatures = signatures;
        newProposal.calldatas = calldatas;
        newProposal.startBlock = temp.startBlock;
        newProposal.endBlock = temp.endBlock;
        newProposal.forVotes = 0;
        newProposal.againstVotes = 0;
        newProposal.abstainVotes = 0;
        newProposal.canceled = false;
        newProposal.executed = false;
        newProposal.vetoed = false;

        latestProposalIds[newProposal.proposer] = newProposal.id;

        /// @notice Maintains backwards compatibility with GovernorBravo events
        emit ProposalCreated(
            newProposal.id,
            msg.sender,
            targets,
            values,
            signatures,
            calldatas,
            newProposal.startBlock,
            newProposal.endBlock,
            description
        );

        /// @notice Updated event with `proposalThreshold` and `quorumVotes`
        emit ProposalCreatedWithRequirements(
            newProposal.id,
            msg.sender,
            targets,
            values,
            signatures,
            calldatas,
            newProposal.startBlock,
            newProposal.endBlock,
            newProposal.proposalThreshold,
            newProposal.quorumVotes,
            description
        );

        return newProposal.id;
}
```
:::

**概要**
新しい提案を行う関数。
提案者は提案閾値を上回る委任を持っている必要があります。
提案の詳細な情報（対象アドレス、Ether値、関数シグネチャ、calldataなど）と提案の説明文が必要です。
提案が受け入れられると、新しい提案の提案IDが返されます。

**詳細**
1. 提案者の委任投票数が提案閾値を上回っていることを確認します。
2. 提案関数情報の引数の数が一致していることを確認します。
3. 提案関数情報が空でないことを確認します。
4. 提案関数情報の数が最大操作数を超えていないことを確認します。
5. 提案者が既にアクティブまたはペンディングの提案を持っている場合、新しい提案を行うことはできません。
6. 提案の投票期間（`startBlockからendBlock`まで）を設定します。
7. 提案の情報を格納し、提案IDを更新します。
8. 提案作成イベントを発行します。

**引数**
- `targets`
	- 提案コールの対象アドレスの配列。
- `values`
	- 提案コールのEther値の配列。
- `signatures`
	- 提案コールの関数シグネチャの配列。
- `calldatas`
	- 提案コールのcalldataの配列。
- `description`
	- 提案の説明文。

**戻り値**
- `uint256`
	- 新しい提案の提案ID。

---

### queue

:::details queue
```solidity
function queue(uint256 proposalId) external {
	require(
	    state(proposalId) == ProposalState.Succeeded,
	    'NounsDAO::queue: proposal can only be queued if it is succeeded'
	);
	Proposal storage proposal = proposals[proposalId];
	uint256 eta = block.timestamp + timelock.delay();
	for (uint256 i = 0; i < proposal.targets.length; i++) {
	    queueOrRevertInternal(
		proposal.targets[i],
		proposal.values[i],
		proposal.signatures[i],
		proposal.calldatas[i],
		eta
	    );
	}
	proposal.eta = eta;
	emit ProposalQueued(proposalId, eta);
}
```
:::

**概要**
成功した提案をキューに入れる関数。

**詳細**
1. 提案が成功した状態であることを確認します。
2. 提案の各アクションをキューに入れます。
3. キューされる操作の実行予定時刻（`eta`）を設定します。
4. 提案がキューに入ったことを示すイベントを発行します。

**引数**
- `proposalId`
	- キューに入れる提案のID。

---

### execute

:::details execute
```solidity
function execute(uint256 proposalId) external {
        require(
            state(proposalId) == ProposalState.Queued,
            'NounsDAO::execute: proposal can only be executed if it is queued'
        );
        Proposal storage proposal = proposals[proposalId];
        proposal.executed = true;
        for (uint256 i = 0; i < proposal.targets.length; i++) {
            timelock.executeTransaction(
                proposal.targets[i],
                proposal.values[i],
                proposal.signatures[i],
                proposal.calldatas[i],
                proposal.eta
            );
        }
        emit ProposalExecuted(proposalId);
}
```
:::

**概要**
`eta`が経過した場合にキューに入れた提案を実行する関数。

**詳細**
1. 提案がキューに入った状態であることを確認します。
2. 提案の各アクションを実行します。
3. 提案が実行されたことを示すイベントを発行します。

**引数**
- `proposalId`
	- 実行する提案のID。

---

### cancel

:::details cancel
```solidity
function cancel(uint256 proposalId) external {
        require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal');

        Proposal storage proposal = proposals[proposalId];
        require(
            msg.sender == proposal.proposer ||
                nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold,
            'NounsDAO::cancel: proposer above threshold'
        );

        proposal.canceled = true;
        for (uint256 i = 0; i < proposal.targets.length; i++) {
            timelock.cancelTransaction(
                proposal.targets[i],
                proposal.values[i],
                proposal.signatures[i],
                proposal.calldatas[i],
                proposal.eta
            );
        }

        emit ProposalCanceled(proposalId);
}
```
:::

**概要**
提案者が閾値を下回るか、提案者の委任が提案閾値を下回った場合にのみ提案をキャンセルする関数。

**詳細**
1. 提案が実行済みでないことを確認します。
2. 提案者が提案をキャンセルできる条件を満たしているかを確認します。
3. 提案の各アクションをキャンセルします。
4. 提案がキャンセルされたことを示すイベントを発行します。

**引数**
- `proposalId`
	- キャンセルする提案のID。

---

### queueOrRevertInternal

:::details queueOrRevertInternal
```solidity
function queueOrRevertInternal(
        address target,
        uint256 value,
        string memory signature,
        bytes memory data,
        uint256 eta
    ) internal {
        require(
            !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
            'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta'
        );
        timelock.queueTransaction(target, value, signature, data, eta);
}
```
:::

**概要**
同じ操作が既に同じ`eta`でキューに入っている場合、操作をキューに入れるかリバートするために使用する関数。

**詳細**
1. 同じ操作が既に同じ`eta`でキューに入っているかどうかを確認します。
2. もし同じ操作が既にキューに入っている場合、エラーを発生させてリバートします。
3. もし同じ操作がキューに入っていない場合、操作をキューに入れます。

**引数**
- `target`
	- 操作対象のアドレス。
- `value`
	- Etherの値。
- `signature`
	- 関数シグネチャ。
- `data`
	- calldata。
- `eta`
	- 実行予定時刻。

---

### veto

:::details veto
```solidity
function veto(uint256 proposalId) external {
        require(vetoer != address(0), 'NounsDAO::veto: veto power burned');
        require(msg.sender == vetoer, 'NounsDAO::veto: only vetoer');
        require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal');

        Proposal storage proposal = proposals[proposalId];

        proposal.vetoed = true;
        for (uint256 i = 0; i < proposal.targets.length; i++) {
            timelock.cancelTransaction(
                proposal.targets[i],
                proposal.values[i],
                proposal.signatures[i],
                proposal.calldatas[i],
                proposal.eta
            );
        }

        emit ProposalVetoed(proposalId);
}
```
:::

**概要**
提案者が拒否権を持ち、提案が実行されていない場合にのみ提案を拒否する関数。

**詳細**
1. 拒否権を持っていることを確認します。
2. 呼び出し元が拒否権を持っている提案者であることを確認します。
3. 提案が実行されていないことを確認します。
4. 提案の各アクションをキャンセルして提案を拒否します。
5. 提案が拒否されたことを示すイベントを発行します。

**引数**
- `proposalId`
	- 拒否する提案のID。

---

### getActions

:::details getActions
```solidity
function getActions(uint256 proposalId)
        external
        view
        returns (
            address[] memory targets,
            uint256[] memory values,
            string[] memory signatures,
            bytes[] memory calldatas
        )
    {
        Proposal storage p = proposals[proposalId];
        return (p.targets, p.values, p.signatures, p.calldatas);
}
```
:::

**概要**
特定の提案のアクション（対象アドレス、Etherの値、関数シグネチャ、calldata）を取得する関数。

**詳細**
1. 提案IDを使用して提案の情報を取得します。
2. 提案のアクション情報を返します。

**引数**
- `proposalId`
	- 提案のID。

**戻り値**
- `address[]`
	- 対象アドレスの配列。
- `uint256[]`
	- Etherの値の配列。
- `string[]`
	- 関数シグネチャの配列。
- `bytes[]`
	- calldataの配列。

---

### getReceipt

:::details getReceipt
```solidity
function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory) {
        return proposals[proposalId].receipts[voter];
}
```
:::

**概要**
特定の提案の特定の投票者の投票レシートを取得する関数。

**詳細**
1. 提案IDと投票者のアドレスを使用して、対応する提案の投票レシートを取得します。

**引数**
- `proposalId`
	- 提案のID。
- `voter`
	- 投票者のアドレス。

**戻り値**
- `Receipt`
	- 投票レシート。

---

### state

:::details state
```solidity
function state(uint256 proposalId) public view returns (ProposalState) {
        require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id');
        Proposal storage proposal = proposals[proposalId];
        if (proposal.vetoed) {
            return ProposalState.Vetoed;
        } else if (proposal.canceled) {
            return ProposalState.Canceled;
        } else if (block.number <= proposal.startBlock) {
            return ProposalState.Pending;
        } else if (block.number <= proposal.endBlock) {
            return ProposalState.Active;
        } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < proposal.quorumVotes) {
            return ProposalState.Defeated;
        } else if (proposal.eta == 0) {
            return ProposalState.Succeeded;
        } else if (proposal.executed) {
            return ProposalState.Executed;
        } else if (block.timestamp >= proposal.eta + timelock.GRACE_PERIOD()) {
            return ProposalState.Expired;
        } else {
            return ProposalState.Queued;
        }
}
```
:::

**概要**
特定の提案の状態を取得する関数。

**詳細**
1. 提案IDを使用して提案の情報を取得します。
2. 提案の状態に基づいて、提案の状態を返します。

**引数**
- `proposalId`
	- 提案のID。

**戻り値**
- `ProposalState`
	- 提案の状態。

---

### castVote

:::details castVote
```solidity
function castVote(uint256 proposalId, uint8 support) external {
        emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), '');
}
```
:::

**概要**
提案に対して投票を行う関数。

**詳細**
1. 投票者のアドレス、提案ID、支持値を使用して、`castVoteInternal`関数を呼び出します。
2. 投票の詳細情報を含むイベントを発行します。

**引数**
- `proposalId`
	- 投票する提案のID。
- `support`
	- 投票の支持値。
	- `0`=反対、`1`=賛成、`2`=棄権。

---

### castVoteWithReason

:::details castVoteWithReason
```solidity
function castVoteWithReason(
        uint256 proposalId,
        uint8 support,
        string calldata reason
    ) external {
        emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason);
}
```
:::

**概要**
理由を付けて提案に対して投票を行う関数。

**詳細**
1. 投票者のアドレス、提案ID、支持値を使用して、`castVoteInternal`関数を呼び出します。
2. 投票の詳細情報と理由を含むイベントを発行します。

**引数**
- `proposalId`
	- 投票する提案のID。
- `support`
	- 投票の支持値。
	- `0`=反対、`1`=賛成、`2`=棄権。
- `reason`
	- 投票者による投票理由。

---

### castVoteBySig

:::details castVoteBySig
```solidity
function castVoteBySig(
        uint256 proposalId,
        uint8 support,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        bytes32 domainSeparator = keccak256(
            abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))
        );
        bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
        bytes32 digest = keccak256(abi.encodePacked('\x19\x01', domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature');
        emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), '');
}
```
:::

**概要**
**EIP712**署名を使用して提案に対して投票を行う関数。

**詳細**
1. ドメインセパレータと構造ハッシュを生成して署名の検証に使用します。
2. 署名を使用して署名者のアドレスを復元し、無効な署名の場合はエラーを発生させます。
3. 投票者のアドレス、提案ID、支持値を使用して、`castVoteInternal`関数を呼び出します。
4. 投票の詳細情報を含むイベントを発行します。

**引数**
- `proposalId`
	- 投票する提案のID。
- `support`
	- 投票の支持値。
	- `0`=反対、`1`=賛成、`2`=棄権。
- `v`
	- 署名の`v`値。
- `r`
	- 署名の`r`値。
- `s`
	- 署名の`s`値。

---

### castVoteInternal

:::details castVoteInternal
```solidity
function castVoteInternal(
        address voter,
        uint256 proposalId,
        uint8 support
    ) internal returns (uint96) {
        require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed');
        require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type');
        Proposal storage proposal = proposals[proposalId];
        Receipt storage receipt = proposal.receipts[voter];
        require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');

        /// @notice: Unlike GovernerBravo, votes are considered from the block the proposal was created in order to normalize quorumVotes and proposalThreshold metrics
        uint96 votes = nouns.getPriorVotes(voter, proposal.startBlock - votingDelay);

        if (support == 0) {
            proposal.againstVotes = proposal.againstVotes + votes;
        } else if (support == 1) {
            proposal.forVotes = proposal.forVotes + votes;
        } else if (support == 2) {
            proposal.abstainVotes = proposal.abstainVotes + votes;
        }

        receipt.hasVoted = true;
        receipt.support = support;
        receipt.votes = votes;

        return votes;
}
```
:::

**概要**
投票のロジックを実行する関数。

**詳細**
1. 提案がアクティブな状態かどうかを確認します。
2. 投票の支持値が`0`から`2`の範囲内にあることを確認します。
3. 提案情報と投票者の情報を使用して、投票が既に行われていないかを確認します。
4. 投票者の過去の投票数を取得します。
5. 投票の支持値に応じて、提案の賛成票、反対票、棄権票を更新します。
6. 投票レシートを更新します。
7. 投票された票の数を返します。

**引数**
- `voter`
	- 投票を行う投票者のアドレス。
- `proposalId`
	- 投票する提案のID。
- `support`
	- 投票の支持値。
	- `0`=反対、`1`=賛成、`2`=棄権。

**戻り値**
- `uint96`
	- 投票された票の数。

---

### _setVotingDelay

:::details _setVotingDelay
```solidity
function _setVotingDelay(uint256 newVotingDelay) external {
        require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only');
        require(
            newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY,
            'NounsDAO::_setVotingDelay: invalid voting delay'
        );
        uint256 oldVotingDelay = votingDelay;
        votingDelay = newVotingDelay;

        emit VotingDelaySet(oldVotingDelay, votingDelay);
}
```
:::

**概要**
管理者が投票遅延時間を変更する関数。

**詳細**
1. 関数を呼び出すユーザーが管理者であることを確認します。
2. 新しい投票遅延時間が適切な範囲内かを確認します。
3. 現在の投票遅延時間を変更し、古い値と新しい値を含むイベントを発行します。

**引数**
- `newVotingDelay`
	- 新しい投票遅延時間（ブロック数）。

---

### _setVotingPeriod

:::details _setVotingPeriod
```solidity
function _setVotingPeriod(uint256 newVotingPeriod) external {
        require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only');
        require(
            newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD,
            'NounsDAO::_setVotingPeriod: invalid voting period'
        );
        uint256 oldVotingPeriod = votingPeriod;
        votingPeriod = newVotingPeriod;

        emit VotingPeriodSet(oldVotingPeriod, votingPeriod);
}
```
:::

**概要**
管理者が投票期間を変更する関数。

**詳細**
1. 関数を呼び出すユーザーが管理者であることを確認します。
2. 新しい投票期間が適切な範囲内かを確認します。
3. 現在の投票期間を変更し、古い値と新しい値を含むイベントを発行します。

**引数**
- `newVotingPeriod`
	- 新しい投票期間（ブロック数）。

---

### _setProposalThresholdBPS

:::details _setProposalThresholdBPS
```solidity
function _setProposalThresholdBPS(uint256 newProposalThresholdBPS) external {
        require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only');
        require(
            newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS &&
                newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS,
            'NounsDAO::_setProposalThreshold: invalid proposal threshold'
        );
        uint256 oldProposalThresholdBPS = proposalThresholdBPS;
        proposalThresholdBPS = newProposalThresholdBPS;

        emit ProposalThresholdBPSSet(oldProposalThresholdBPS, proposalThresholdBPS);
}
```
:::

**概要**
管理者が提案閾値基準ポイントを設定する関数。

**詳細**
1. 関数を呼び出すユーザーが管理者であることを確認します。
2. 新しい提案閾値が適切な範囲内かを確認します。
3. 現在の提案閾値を変更し、古い値と新しい値を含むイベントを発行します。

**引数**
- `newProposalThresholdBPS`
	- 新しい提案閾値。

---

### _setQuorumVotesBPS

:::details _setQuorumVotesBPS
```solidity
function _setQuorumVotesBPS(uint256 newQuorumVotesBPS) external {
        require(msg.sender == admin, 'NounsDAO::_setQuorumVotesBPS: admin only');
        require(
            newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS,
            'NounsDAO::_setProposalThreshold: invalid proposal threshold'
        );
        uint256 oldQuorumVotesBPS = quorumVotesBPS;
        quorumVotesBPS = newQuorumVotesBPS;

        emit QuorumVotesBPSSet(oldQuorumVotesBPS, quorumVotesBPS);
}
```
:::

**概要**
管理者がクオーラム投票基準ポイントを設定する関数。

**詳細**
1. 関数を呼び出すユーザーが管理者であることを確認します。
2. 新しいクオーラム投票基準ポイントが適切な範囲内かを確認します。
3. 現在のクオーラム投票基準ポイントを変更し、古い値と新しい値を含むイベントを発行します。

**引数**
- `newQuorumVotesBPS`
	- 新しいクオーラム投票基準ポイント。

---

### _setPendingAdmin

:::details _setPendingAdmin
```solidity
function _setPendingAdmin(address newPendingAdmin) external {
        // Check caller = admin
        require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only');

        // Save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;

        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;

        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
}
```
:::

**概要**
管理者権限の移行を開始するし、新しいペンディング管理者を設定する関数。

**詳細**
1. 関数を呼び出すユーザーが管理者であることを確認します。
2. 現在のペンディング管理者の値を保存しておきます。
3. 新しいペンディング管理者のアドレスを設定します。
4. `NewPendingAdmin`イベントを発行します。

新しいペンディング管理者は、`_acceptAdmin`を呼び出して移行を確定させる必要があります。

**引数**
- `newPendingAdmin`
	- 新しいペンディング管理者のアドレス。

---

### _acceptAdmin

:::details _acceptAdmin
```solidity
function _acceptAdmin() external {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');

        // Save current values for inclusion in log
        address oldAdmin = admin;
        address oldPendingAdmin = pendingAdmin;

        // Store admin with value pendingAdmin
        admin = pendingAdmin;

        // Clear the pending value
        pendingAdmin = address(0);

        emit NewAdmin(oldAdmin, admin);
        emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
}
```
:::

**概要**
ペンディング中の管理者が管理者権限の移行を受け入れて管理者を更新する関数。

**詳細**
1. 呼び出し元がペンディング管理者であることを確認します。
また、`pendingAdmin`が`address(0)`でないことも確認します。
2. 現在の管理者の値とペンディング管理者の値を保存しておきます。
3. 管理者のアドレスをペンディング管理者の値に更新します。
4. `NewAdmin`と`NewPendingAdmin`のイベントを発行します。

---

### _setVetoer

:::details _setVetoer
```solidity
function _setVetoer(address newVetoer) public {
        require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only');

        emit NewVetoer(vetoer, newVetoer);

        vetoer = newVetoer;
}
```
:::

**概要**
`vetoer`アドレスを変更する関数。

**詳細**
1. 関数を呼び出すユーザーが`vetoer`であることを確認します。
2. `NewVetoer`イベントを発行し、古い`vetoer`アドレスと新しい`vetoer`アドレスを含めます。
3. `vetoer`アドレスを新しいアドレスに更新します。

**引数**
- `newVetoer`
	- 新しい`vetoer`アドレス。

---

### _burnVetoPower

:::details _burnVetoPower
```solidity
function _burnVetoPower() public {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');

        _setVetoer(address(0));
}
```
:::

**概要**
`veto`権限を永遠に破棄する関数

**詳細**
1. 関数を呼び出すユーザーが`vetoer`であることを確認します。
2. `_setVetoer`関数を呼び出して`vetoer`アドレスを`address(0)`に設定し、`veto`権限を消失させます。

---

### proposalThreshold

:::details proposalThreshold
```solidity
function proposalThreshold() public view returns (uint256) {
        return bps2Uint(proposalThresholdBPS, nouns.totalSupply());
}
```
:::

**概要**
**Noun**トークンの総供給量を使用して計算される現在の提案閾値を取得する関数。

**詳細**
- 提案閾値は、`bps2Uint`関数を使用して計算されます。
- 現在の提案閾値基準ポイント（`proposalThresholdBPS`）と**Noun**トークンの総供給量を入力として使用します。
- 提案閾値は固定の量ではなく、総供給量に基づいて計算されます。


**戻り値**
- `uint256`
	- 現在の提案閾値。

---

### quorumVotes

:::details quorumVotes
```solidity
function quorumVotes() public view returns (uint256) {
        return bps2Uint(quorumVotesBPS, nouns.totalSupply());
}
```
:::

**概要**
**Noun**トークンの総供給量を使用して計算される現在のクオーラム投票数を取得する関数。

**詳細**
- クオーラム投票数は、`bps2Uint`関数を使用して計算されます。
- 現在のクオーラム投票基準ポイント（`quorumVotesBPS`）と**Noun**トークンの総供給量を入力として使用します。
- クオーラム投票数は固定の量ではなく、総供給量に基づいて計算されます。

**戻り値**
- `uint256`
	- 現在のクオーラム投票数。

---

### bps2Uint

:::details bps2Uint
```solidity
function bps2Uint(uint256 bps, uint256 number) internal pure returns (uint256) {
        return (number * bps) / 10000;
}
```
:::

**概要**
基準ポイント（Basis Points）を整数に変換する関数。

**詳細**
- 基準ポイントは、パーセンテージを`10000`で割った値です。
	- 例えば、`100%`は`10000`の基準ポイントに対応します。
- `number`と`bps`の積を`10000`で割って整数に変換します。

**引数**
- `bps`
	- 基準ポイント。
- `number`
	- 数値。

**戻り値**
- `uint256`
	- 変換された整数。

---

### getChainIdInternal

:::details getChainIdInternal
```solidity
function getChainIdInternal() internal view returns (uint256) {
        uint256 chainId;
        assembly {
            chainId := chainid()
        }
        return chainId;
}
```
:::

**概要**
内部でチェーンIDを取得する関数。

**詳細**
- アセンブリコードを使用して、チェーンIDを取得します。

**戻り値**
- `uint256`
	- チェーンID。

---

# コード

## NounsDAOLogicV1

- `/governance/NounsDAOLogicV1.sol`

## NounsDAOInterfaces

- `/governance/NounsDAOInterfaces.sol`

# 最後に

今回の記事では、Bunzzの新機能『**DeCipher**』を使用して、**Nouns**の「**NounsDAOLogicV1**」のコントラクトを見てきました。
いかがだったでしょうか？
今後も特定のNFTやコントラクトをピックアップしてまとめて行きたいと思います。

普段はブログやQiitaでブロックチェーンやAIに関する記事を挙げているので、よければ見ていってください！

https://chaldene.net/

https://qiita.com/cardene
