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

> NounsTokenコントラクトは、Nouns DAOプロジェクトの中心的なコントラクトです。Nouns DAOは、非代替可能トークン（NFT）と呼ばれるユニークなデジタルアセットを作成し管理する分散型自治組織（DAO）です。

- URL: https://cardene.dev/articles/6d806a814f0720
- 公開日: 2023-08-29
- 更新日: 2023-08-29
- トピック: Blockchain

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

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

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

https://www.bunzz.dev/decipher

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

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

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

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

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

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

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

# 概要

**NounsToken**コントラクトは、**Nouns DAO**プロジェクトの中心的なコントラクトです。
**Nouns DAO**は、非代替可能トークン（NFT）と呼ばれるユニークなデジタルアセットを作成し管理する分散型自治組織（DAO）です。

## NounsTokenコントラクトの目的
**NounsToken**コントラクトは、Ethereumブロックチェーン上に存在するプログラムです。
このコントラクトは、「**Noun**」と呼ばれる特別なNFTの作成、所有権の管理、そして転送を担当しています。
各**Noun**はそれぞれ異なり、コントラクト内で一意の番号（トークンID）で区別されます。
また、**NounsToken**コントラクトは、**Noun**を不要になった時に破棄（燃焼）する機能も持っています。

このコントラクトは、**ERC721**という規格に従って作られています。
**ERC721**は、Ethereumブロックチェーン上でNFTを作成し管理するための共通のルールです。
これにより、**Noun**は**ERC721**トークンをサポートする他のコントラクトやプラットフォームとも連携できます。
例えば、NFTを買ったり売ったりできるデジタルマーケットプレイスでも利用されます。

## NounsTokenコントラクトの役割

### 1. Nounのミンティング（作成）
**NounsToken**コントラクトは新しい**Noun**を作成する機能を持っています。
これは`mint`できるアドレスである「`minter`」によって制御されます。
`minter`は、新しい**Noun**を作成する唯一のエンティティです。
つまり、新しい**Noun**が作られる際には`minter`によって管理されます。

### 2. 所有権の管理
コントラクトは、各**Noun**の所有者を追跡して管理します。
**Noun**が一人から別の人へ移動する際には、コントラクトが所有権の記録を更新します。
これにより、**Noun**の所有者が変わったときでも正確な情報が保持されます。

### 3. Nounの燃焼（破棄）
コントラクトは、**Noun**を破壊して存在を消すこともできます。
この操作を「**バーン**」と呼びます。
不要な**Noun**を取り除くために使用されることがあります。

### 4. ミンターのロック
コントラクトには、`minter`をロックする機能があります。
これにより、新しい**Noun**を作成できる権限を変更することを防ぐことができます。
一度ロックされると、`minter`の変更が制限されます。

### 5. ディスクリプタとシーダーの設定
コントラクトには、ディスクリプタとシーダーを設定しロックする機能もあります。
ディスクリプタは、各**Noun**に関連する詳細情報を生成します。
これには**Noun**の名前、説明、画像などが含まれます。
シーダーは、各**Noun**に対してユニークなシード（種）を生成し、それを用いて**Noun**の独自の外観や特性を作成します。

これらの要素を通じて、**NounsToken**コントラクトは**Noun**の作成から管理、転送、破壊までのさまざまな重要な機能を提供しています。

:::message
**Descriptor**

ディスクリプタは、**Noun**のトークンURI（Uniform Resource Identifier）を生成するためのコントラクトです。
**Noun**のIDとシード（ランダムな要素）を入力として受け取り、それに基づいてトークンURIを生成します。
トークンURIには、**Noun**に関連する情報やメタデータが含まれます。

**Seeder**
**Noun**のシードを生成するためのコントラクトです。
シードはランダムな要素であり、**Noun**の特性や見た目を一意に決定します。
**NounsToken**コントラクトでは、新しい**Noun**をミンティングする際にシーダーが使用されます。
シーダーはNounのIDとディスクリプタを入力として受け取り、それに基づいてランダムなシードを生成します。
このシードは、**Noun**の生成プロセスにおいてランダム性を導入し、**Noun**同士が異なる特性を持つようにします。

ディスクリプタとシーダーの組み合わせにより、Nounの生成と表示に関連する要素が制御され、多様性と個性が生まれることを可能にします。
:::

## Web3、ブロックチェーン、dAppの観点からNounsTokenコントラクトを理解する

### Web3の観点
Web3は、ブロックチェーン技術を活用して分散型アプリケーション（dApp）を構築するための技術です。
**NounsToken**コントラクトは、dAppのバックエンドロジックやデータストレージを提供します。
ユーザーはWeb3を介して**NounsToken**コントラクトとやり取りし、**Noun**の作成や所有権の管理などの操作を行うことができます。
これによって、分散化された透明性のあるアプリケーションが実現されます。

### ブロックチェーンの観点
**NounsToken**コントラクトは、Ethereumブロックチェーン上で不変のスマートコントラクトとして存在します。
ブロックチェーンは分散化された台帳であり、**NounsToken**コントラクトによるすべてのアクションはブロックチェーン上に永久に記録されます。
これにより、**Noun**の作成、転送、バーンなどの過程が透明かつ改ざん不可能になります。

### dAppの観点
dAppは、ユーザーが直接操作する分散型アプリケーションです。
**NounsToken**コントラクトは、このdAppのエンジンとなる部分です。
ユーザーはdAppを通じて**NounsToken**コントラクトの機能を利用し、**Noun**の作成や所有権の管理、転送、バーンなどを行うことができます。
**Nouns DAO**プロジェクトの中心的な機能を提供する役割を果たしています。

## 関連リンク

- [Nouns DAO](https://www.alchemy.com/popular-smart-contracts/nouns-dao#)
- [NounsToken contract](https://www.alchemy.com/smart-contracts/nounstoken)
- [LayerZero](https://www.alchemy.com/popular-smart-contracts/layerzero)
- [0x Protocol](https://www.alchemy.com/popular-smart-contracts/0x-protocol)
- [Bored Ape Yacht Club](https://www.alchemy.com/popular-smart-contracts/bored-ape-yacht-club)

# 使い方

開発者の視点から**NounsToken**コントラクトを使用する手順をステップバイステップで説明していきます。
**NounsToken**コントラクトは、トークンの発行、転送、バーンなどを管理し、`minter`、`descriptor`、`seeder`を設定・ロックします。

:::message
**1. mint（作成）**
- `minter`のアドレスを確認し、新しい**Noun**を作成できる権限があることを確認します。
- 新しい**Noun**を作成したい場合、特定の関数を呼び出してトランザクションを送信します。
- トランザクションが承認されると、新しい**Noun**が作成され、一意のトークンIDが割り当てられます。

**2. 所有権の管理と転送**
- 各**Noun**の所有者を確認するための関数を使用します。
- **Noun**の所有者が変わる場合、トランザクションを使用して所有権の転送を行います。
- これにより、新しい所有者のアドレスが記録されます。

**3. Nounのバーン**
- 不要な**Noun**を破棄したい場合、該当する関数を使用してトランザクションを送信します。
- トランザクションが承認されると、**Noun**は破壊され、存在しない状態になります。

**4. minter、ディスクリプタ、シーダーの設定**
- `minter`アドレスを変更したい場合、関連する関数を呼び出してトランザクションを送信します。
- ディスクリプタやシーダーの設定を変更したい場合も、対応する関数を使用します。
- これにより、**Noun**のメタデータや独自の特性を調整できます。

**5. 設定のロック**
- `minter`、ディスクリプタ、シーダーの設定を変更できないようにする場合、関連するロック関数を使用します。
- これにより、設定変更が制限されます。
:::

### NounsTokenコントラクトの使用手順

1. **コントラクトのセットアップ**
**NounsToken**コントラクトをEthereumネットワークに展開します。
これは、**Truffle**や**Hardhat**などのツールを使用して行います。

2. **Nounders DAOアドレスの設定**
`setNoundersDAO(address noundersDAO)`関数を呼び出し、**Nounders DAO**のアドレスを指定します。
これにより、**Noun**をミンティングできる権限を持つ組織が設定されます。

3. **minterの設定**
`setMinter(address minter)`関数を呼び出し、`minter`のアドレスを指定します。
これにより、**Noun**を発行できる権限を持つアドレスが設定されます。
`minter`の変更を防ぎたい場合は、`lockMinter()`関数を呼び出して`minter`をロックします。
これにより、将来の更新が制限されます。

4. **ディスクリプタの設定**
`setDescriptor(INounsDescriptor descriptor)`関数を呼び出し、ディスクリプタのアドレスを指定します。
これにより、各**Noun**のトークンURIを生成するディスクリプタが設定されます。
ディスクリプタの変更を防ぎたい場合は、`lockDescriptor()`関数を呼び出してディスクリプタをロックします。
これにより、将来の更新が制限されます。

5. **シーダーの設定**
`setSeeder(INounsSeeder seeder)`関数を呼び出し、シーダーのアドレスを指定します。
これにより、各**Noun**のシードを生成するシーダーが設定されます。
シーダーの変更を防ぎたい場合は、`lockSeeder()`関数を呼び出してシーダーをロックします。
これにより、将来の更新が制限されます。

6. **Nounのミント**
`mint()`関数を呼び出して新しい**Noun**を発行します。
この関数は新しく発行された**Noun**のトークンIDを返します。

7. **Nounのバーン**
`burn(uint256 tokenId)`関数を呼び出し、バーンさせたい**Noun**のトークンIDを指定します。
これにより、指定した**Noun**が破棄され、流通から削除されます。

8. **NounのデータURIの取得**
`dataURI(uint256 tokenId)`関数を呼び出し、指定した**Noun**のトークンIDを指定します。
これにより、指定した**Noun**のデータURIが返されます。

**⚠️ 注意**
実際にメインネットにデプロイする前に、必ずテストネットでコントラクトの操作を試して、エラーやバグによる資金の損失を避けるようにしてください。

# パラメータ

## _noundersDAO

**Nounders DAO**コントラクトのアドレス。

## _minter
ミントできるアドレス。

## _descriptor
**NounsDescriptor**コントラクトのアドレス。

## _seeder
**NounsSeeder**コントラクトのアドレス。

## _proxyRegistry
**ProxyRegistry**コントラクトのアドレス。

# コントラクト

## NounsToken

### noundersDAO

:::details noundersDAO
```solidity
address public noundersDAO;
```
:::

**概要**
**Nounders DAO**のコントラクトアドレス。

---

### minter

:::details minter
```solidity
address public minter;
```
:::

**概要**
**Nouns**をミント（作成）する権限を持つアドレス。

---

### descriptor

:::details descriptor
```solidity
INounsDescriptor public descriptor;
```
:::

**概要**
**Noun**のトークンURI（メタデータ）を生成するためのディスクリプタのアドレス。

---

### seeder

:::details seeder
```solidity
INounsSeeder public seeder;
```
:::

**概要**
**Noun**のシードを生成するためのシーダーのアドレス。

**詳細**
この変数には、Nounの独自の特性や属性を生成するためのシーダーのアドレスが格納されます。

---

### isMinterLocked

:::details isMinterLocked
```solidity
bool public isMinterLocked;
```
:::

**概要**
`minter`のアドレスがロックされているかどうかを示す。

**詳細**
この変数はブール値（真偽値）を持ち、`true`の場合は`minter`のアドレスがロックされていることを示します。
ロックされたミンターのアドレスは変更できなくなります。

---

### isDescriptorLocked

:::details isDescriptorLocked
```solidity
bool public isDescriptorLocked;
```
:::

**概要**
ディスクリプタのアドレスがロックされているかどうかを示す。

**詳細**
この変数はブール値（真偽値）を持ち、`true`の場合はディスクリプタのアドレスがロックされていることを示します。
ロックされたディスクリプタのアドレスは変更できなくなります。

---

### isSeederLocked

:::details isSeederLocked
```solidity
bool public isSeederLocked;
```
:::

**概要**
シーダーのアドレスがロックされているかどうかを示す。

**詳細**
この変数はブール値（真偽値）を持ち、`true`の場合はシーダーのアドレスがロックされていることを示します。
ロックされたシーダーのアドレスは変更できなくなります。

---

### seeds

:::details seeds
```solidity
mapping(uint256 => INounsSeeder.Seed) public seeds;
```
:::

**概要**
**Noun**のシード情報を格納するマッピング変数。

**詳細**
各**Noun**のトークンIDに対するシード情報を保持します。
シード情報には独自の属性や特性が含まれます。

---

### _currentNounId

:::details _currentNounId
```solidity
uint256 private _currentNounId;
```
:::

**概要**
現在の**Noun**のID。

**詳細**
新しい**Noun**がミントされるたびに、このIDが増加します。

---

### _contractURIHash

:::details _contractURIHash
```solidity
string private _contractURIHash = 'QmZi1n79FqWt2tTLwCqiy6nLM6xLGRsEPQ5JmReJQKNNzX';
```
:::

**概要**
コントラクトレベルのメタデータのIPFSコンテンツハッシュ。

**詳細**
コントラクト全体に関連するメタデータのIPFSコンテンツハッシュが格納されます。
このハッシュはコントラクトの詳細な情報を識別します。

---

### proxyRegistry

:::details proxyRegistry
```solidity
IProxyRegistry public immutable proxyRegistry;
```
:::

**概要**
**OpenSeaのProxy Registry**のコントラクトアドレス。

**詳細**
**OpenSeaのProxy Registry**のスマートコントラクトのアドレスが格納されます。
OpenSeaのNFTプラットフォームとの連携に使用されます。

---

### whenMinterNotLocked

:::details whenMinterNotLocked
```solidity
modifier whenMinterNotLocked() {
    require(!isMinterLocked, 'Minter is locked');
    _;
}
```
:::

**概要**
`minter`がロックされていないことを確認するための修飾子。

**詳細**
`minter`がロックされていない場合にのみ関数の実行を許可します。
もしミンターがロックされている場合、エラーメッセージが表示されます。

---

### whenDescriptorNotLocked

:::details whenDescriptorNotLocked
```solidity
modifier whenDescriptorNotLocked() {
    require(!isDescriptorLocked, 'Descriptor is locked');
    _;
}
```
:::

**概要**
ディスクリプタがロックされていないことを確認するための修飾子。

**詳細**
ディスクリプタがロックされていない場合にのみ関数の実行を許可します。
もしディスクリプタがロックされている場合、エラーメッセージが表示されます。

---

### whenSeederNotLocked

:::details whenSeederNotLocked
```solidity
modifier whenSeederNotLocked() {
    require(!isSeederLocked, 'Seeder is locked');
    _;
}
```
:::

**概要**
シーダーがロックされていないことを確認するための修飾子。

**詳細**
シーダーがロックされていない場合にのみ関数の実行を許可します。
もしシーダーがロックされている場合、エラーメッセージが表示されます。

---

### onlyNoundersDAO

:::details onlyNoundersDAO
```solidity
modifier onlyNoundersDAO() {
    require(msg.sender == noundersDAO, 'Sender is not the nounders DAO');
    _;
}
```
:::

**概要**
送信者が**nounders DAO**であることを確認するための修飾子。

**詳細**
送信者が**nounders DAO**のアドレスと一致する場合にのみ関数の実行を許可します。
一致しない場合、エラーメッセージが表示されます。

---

### onlyMinter

:::details onlyMinter
```solidity
modifier onlyMinter() {
    require(msg.sender == minter, 'Sender is not the minter');
    _;
}
```
:::

**概要**
送信者が`minter`であることを確認するための修飾子。

**詳細**
送信者がミンターのアドレスと一致する場合にのみ関数の実行を許可します。
一致しない場合、エラーメッセージが表示されます。

---

### contractURI

:::details contractURI
```solidity
function contractURI() public view returns (string memory) {
    return string(abi.encodePacked('ipfs://', _contractURIHash));
}
```
:::

**概要**
コントラクトのメタデータのIPFS URIを取得する関数。

**詳細**
この関数は、IPFS URIの形式でコントラクトのメタデータのURIを返します。
メタデータは、コントラクトに関する追加情報を提供します。

**戻り値**
- `memory`
	- コントラクトのメタデータのIPFS URI。

---

### setContractURIHash

:::details setContractURIHash
```solidity
function setContractURIHash(string memory newContractURIHash) external onlyOwner {
    _contractURIHash = newContractURIHash;
}
```
:::

**概要**
`_contractURIHash`を設定する関数。
コントラクトのオーナーのみが呼び出すことができます。

**詳細**
新しいコントラクトのメタデータのIPFS URIハッシュを設定します。
メタデータのURIはIPFSのハッシュとして保存されます。

**引数**
- `newContractURIHash`
	- 新しいコントラクトのメタデータのIPFS URIハッシュ。

**戻り値**
なし

---

### isApprovedForAll

:::details isApprovedForAll
```solidity
function isApprovedForAll(address owner, address operator) public view override(IERC721, ERC721) returns (bool) {
    if (proxyRegistry.proxies(owner) == operator) {
        return true;
    }
    return super.isApprovedForAll(owner, operator);
}
```
:::

**概要**
所有者とオペレーターのアドレスを受け取り、所有者がOpenSeaのプロキシコントラクトのオペレーターか確認する関数。

**詳細**
所有者とオペレーターのアドレスを受け取り、所有者がOpenSeaのプロキシコントラクトのオペレーターであれば、`true`を返します。
それ以外の場合は、親クラスの`isApprovedForAll`関数を呼び出して結果を返します。

**引数**
- `owner`
	- 所有者のアドレス。
- `operator`
	- オペレーターのアドレス。

**戻り値**
- `bool`
	- 承認されているかどうかを示す真偽値。

---

### mint

:::details mint
```solidity
function mint() public override onlyMinter returns (uint256) {
    if (_currentNounId <= 1820 && _currentNounId % 10 == 0) {
        _mintTo(noundersDAO, _currentNounId++);
    }
    return _mintTo(minter, _currentNounId++);
}
```
:::

**概要**
**Noun**をミントする関数。

**詳細**
現在のNoun IDが`1820`以下であり、かつ`10`で割った余りが`0`の場合、**noundersDAO**の**nounders**に**Noun**をミントします。
それ以外の場合は、`minter`に通常の**Noun**をミントします。
`0`から始まる`10`個の**Noun**ごとに、`183`個の**nounder Noun**（5年間の24時間オークション）が**nounders**にミントされます。

**戻り値**
- `uint256`
	- ミントされた**Noun**のID。

---

### burn

:::details burn
```solidity
function burn(uint256 nounId) public override onlyMinter {
    _burn(nounId);
    emit NounBurned(nounId);
}
```
:::

**概要**
**Noun**を燃やす関数。

**詳細**
指定されたNoun IDの**Noun**を燃やします。
**Noun**が燃やされると、それに関する情報は永久に削除されます。

**引数**
- `nounId`
	- 燃やすNounのID。

**戻り値**
なし

---

### tokenURI

:::details tokenURI
```solidity
function tokenURI(uint256 tokenId) public view override returns (string memory) {
    require

(_exists(tokenId), 'NounsToken: 存在しないトークンのURIクエリ');
    return descriptor.tokenURI(tokenId, seeds[tokenId]);
}
```
:::

**概要**
特定のNFTアセットのための一意の**Uniform Resource Identifier（URI）** を取得する関数。

**詳細**
トークンIDを受け取り、そのトークンに関連するメタデータのURIを返します。
対応する**Noun**が存在しない場合、エラーメッセージが表示されます。

**引数**
- `tokenId`
	- 取得したいトークンのID。

**戻り値**
- `string memory`
	- メタデータのURI。

---

### dataURI

:::details dataURI
```solidity
function dataURI(uint256 tokenId) public view override returns (string memory) {
    require(_exists(tokenId), 'NounsToken: 存在しないトークンのURIクエリ');
    return descriptor.dataURI(tokenId, seeds[tokenId]);
}
```
:::

**概要**
トークンIDを受け取り、そのトークンに関連するデータURIを返す関数。

**詳細**
`tokenURI`に似た関数ですが、常にベース`64`エンコードされたデータURIを提供し、JSONの内容を直接インラインで含みます。
対応する**Noun**が存在しない場合、エラーメッセージが表示されます。

**引数**
- `tokenId`
	- 取得したいトークンのID。

**戻り値**
- `string memory`
	- データURI。

---

### setNoundersDAO

:::details setNoundersDAO
```solidity
function setNoundersDAO(address _noundersDAO) external override onlyNoundersDAO {
    noundersDAO = _noundersDAO;

    emit NoundersDAOUpdated(_noundersDAO);
}
```
:::

**概要**
**nounders DAO**のコントラクトアドレスを設定する関数。
ロックされていない場合にのみ、**nounders DAO**コントラクトがこの関数を呼び出すことができます。

**詳細**
**nounders DAO**のアドレスを受け取り、新しいアドレスに更新します。
更新後に`NoundersDAOUpdated`イベントが発行されます。

**引数**
- `_noundersDAO`
	- 新しい**nounders DAO**のアドレス。

**戻り値**
なし

---

### setMinter

:::details setMinter
```solidity
function setMinter(address _minter) external override onlyOwner whenMinterNotLocked {
    minter = _minter;

    emit MinterUpdated(_minter);
}
```
:::

**概要**
トークンの`minter`を設定する関数。
ロックされていない場合にのみ、コントラクトのオーナーがこの関数を呼び出すことができます。

**詳細**
`minter`のアドレスを受け取り、新しいアドレスに更新します。
更新後に`MinterUpdated`イベントが発行されます。

**引数**
- `_minter`
	- 新しい`miner`のアドレス。

**戻り値**
なし

---

### lockMinter

:::details lockMinter
```solidity
function lockMinter() external override onlyOwner whenMinterNotLocked {
    isMinterLocked = true;

    emit MinterLocked();
}
```
:::

**概要**
`minter`をロックする関数。

**詳細**
実行すると、`minter`がロックされ、以降の変更ができなくなります。
ロックされていない場合にのみ、コントラクトのオーナーがこの関数を呼び出すことができます。
この操作は元に戻すことができず、慎重に実行する必要があります。
ロックされた後に`MinterLocked`イベントが発行されます。

---

### setDescriptor

:::details setDescriptor
```solidity
function setDescriptor(INounsDescriptor _descriptor) external override onlyOwner whenDescriptorNotLocked {
    descriptor = _descriptor;

    emit DescriptorUpdated(_descriptor);
}
```
:::

**概要**
トークンのURIディスクリプタを設定する関数。

**詳細**
ロックされていない場合にのみ、コントラクトのオーナーがこの関数を呼び出すことができます。
設定後に`DescriptorUpdated`イベントが発行されます。

**引数**
- `INounsDescriptor _descriptor`
	- 新しいURIディスクリプタのインターフェース。

**戻り値**
なし

---

### lockDescriptor

:::details lockDescriptor
```solidity
function lockDescriptor() external override onlyOwner whenDescriptorNotLocked {
    isDescriptorLocked = true;

    emit DescriptorLocked();
}
```
:::

**概要**
ディスクリプタをロックする関数。

**詳細**
実行すると、ディスクリプタがロックされ、以降の変更ができなくなります。
ロックされていない場合にのみ、コントラクトのオーナーがこの関数を呼び出すことができます。
この操作は元に戻すことができず、慎重に実行する必要があります。
ロックされた後に`DescriptorLocked`イベントが発行されます。

**戻り値**
なし

---

### setSeeder

:::details setSeeder
```solidity
function setSeeder(INounsSeeder _seeder) external override onlyOwner whenSeederNotLocked {
    seeder = _seeder;

    emit SeederUpdated(_seeder);
}
```
:::

**概要**
トークンのシーダーを設定する関数。

**詳細**
ロックされていない場合にのみ、コントラクトのオーナーがこの関数を呼び出すことができます。
新しいシーダーのインターフェースを受け取り、それを設定します。
設定後に`SeederUpdated`イベントが発行されます。

**引数**
- `INounsSeeder _seeder`
	- 新しいシーダーのインターフェース。

**戻り値**
なし

---

### lockSeeder

:::details lockSeeder
```solidity
function lockSeeder() external override onlyOwner whenSeederNotLocked {
    isSeederLocked = true;

    emit SeederLocked();
}
```
:::

**概要**
シーダーをロックする関数。

**詳細**
実行すると、シーダーがロックされ、以降の変更ができなくなります。
ロックされていない場合にのみ、コントラクトのオーナーがこの関数を呼び出すことができます。
この操作は元に戻すことができず、慎重に実行する必要があります。
ロックされた後に`SeederLocked`イベントが発行されます。

**戻り値**
なし

---

### _mintTo

:::details _mintTo
```solidity
function _mintTo(address to, uint256 nounId) internal returns (uint256) {
    INounsSeeder.Seed memory seed = seeds[nounId] = seeder.generateSeed(nounId, descriptor);

    _mint(owner(), to, nounId);
    emit NounCreated(nounId, seed);

    return nounId;
}
```
:::

**概要**
指定したアドレスに`nounId`の**Noun**をミントする関数。

**詳細**
指定したアドレス**Noun**をミントします
また、それに対応するシードを生成し、`NounCreated`イベントを発行します。

**引数**
- `to`
	- **Noun**をミントするアドレス。
- `nounId`
	- ミントする**Noun**のID。

**戻り値**
- `uint256`
	- ミントされた**Noun**のID。

# イベント

## ERC721Checkpointable

### decimals

:::details decimals
```solidity
uint8 public constant decimals = 0;
```
:::

**概要**
**ERC20**規約に従って小数点以下の桁数を定義。

**詳細**
トークンの小数点以下の桁数を示します。
**ERC20**トークンは一般的に小数点以下の桁数を持つことができますが、このトークンでは小数点以下の桁数を`0`として定義しています。

---

### _delegates

:::details _delegates
```solidity
mapping(address => address) private _delegates;
```
:::

**概要**
各アカウントのデリゲートを記録する配列。

**詳細**
各アカウントのアドレスをキーとして、そのアカウントのデリゲートのアドレスを値として関連付けます。
デリゲートとは、代理投票権を持つアカウントを指します。

---

### Checkpoint

:::details 
```solidity　details
struct Checkpoint {
    uint32 fromBlock;
    uint96 votes;
}
```
:::

**概要**
特定のブロックからの投票数のチェックポイントを示す構造体。

**詳細**
投票数の変化を特定のブロックから記録するために使用されます。
`fromBlock`はチェックポイントが記録されたブロック番号を、`votes`はそのブロックでの投票数を示します。

---

### checkpoints

:::details checkpoints
```solidity
mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;
```
:::

**概要**
各アカウントの投票チェックポイントをインデックスごとに記録するマッピング配列。

**詳細**
各アカウントのアドレスをキーとし、インデックスをサブキーとして、投票チェックポイントを関連付けます。
各チェックポイントは特定のブロックからの投票数を示すものです。

**パラメータ**
- `address`
	- アカウントのアドレス。
- `uint32`
	- チェックポイントのインデックス。

---

### numCheckpoints

:::details numCheckpoints
```solidity
mapping(address => uint32) public numCheckpoints;
```
:::

**概要**
各アカウントの投票チェックポイントの数を記録するマッピング配列。

**詳細**
各アカウントのアドレスをキーとして、投票チェックポイントの数を関連付けます。
投票が行われるたびに新しいチェックポイントが作成され、その数が増加します。

**パラメータ**
- `address`
	- アカウントのアドレス。

---

### DOMAIN_TYPEHASH

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

**概要**
**EIP712**ドメインのタイプハッシュを示します。

**詳細**
**EIP712**は、スマートコントラクトによるメッセージ署名の標準化に関する提案です。
この定数は、**EIP712**メッセージのドメインを定義する際に使用されるタイプハッシュです。

---

### DELEGATION_TYPEHASH

:::details DELEGATION_TYPEHASH
```solidity
bytes32 public constant DELEGATION_TYPEHASH =
    keccak256('Delegation(address delegatee,uint256 nonce,uint256 expiry)');
```
:::

**概要**
コントラクトで使用されるデリゲーション構造体の**EIP712**タイプハッシュを示します。

**詳細**
この定数は、コントラクト内でデリゲーションの署名を作成および検証する際に使用される**EIP712**タイプハッシュです。
デリゲーションは、アカウントが投票権を他のアカウントに委任する操作を指します。

---

### nonces

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

**概要**
アカウントごとの署名やデリゲーションのための`nonce`（トランザクション番号）を記録するマッピング配列。

**詳細**
各アカウントのアドレスをキーとして、`nonce`（トランザクション番号）を関連付けます。
`nonce`は署名やデリゲーション操作のセキュリティを確保するために使用されます。

**パラメータ**
- `address`
	- アカウントのアドレス。

---

### votesToDelegate

:::details votesToDelegate
```solidity
function votesToDelegate(address delegator) public view returns (uint96)
```
:::

**概要**
デリゲータがデリゲートできる投票数を取得する関数。
デリゲータの現在の残高となります。

**詳細**
指定されたデリゲータの現在の残高を取得し、それを`uint96`の形式に変換して返します。

**引数**
- `delegator`
	- デリゲータのアドレス。

**戻り値**
- `uint96`
	- デリゲータがデリゲートできる投票数。

---

### delegates

:::details delegates
```solidity
function delegates(address delegator) public view returns (address)
```
:::

**概要**
デリゲータのアドレスを取得する関数。
デリゲーションされていない場合、デリゲータの自身のアドレスを返します。

**引数**
- `delegator
	-  デリゲータのアドレス。

**戻り値**
- `address`
	- デリゲータのアドレス。

---

### _beforeTokenTransfer

:::details _beforeTokenTransfer 
```solidity
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override
```
:::

**概要**
トークンの送信に伴ってデリゲートの投票数を更新する関数。

**詳細**
トークンの送信前に呼び出されます。
トークンの送信に伴って、送信元アドレスと送信先アドレスのデリゲートの投票数を更新します。

**引数**
- `from`
	- トークンの送信元アドレス。
- `to`
	- トークンの送信先アドレス。
- `tokenId`
	- トークンのID。

**戻り値**
なし

---

### delegate

:::details delegate
```solidity
function delegate(address delegatee) public
```
:::

**概要**
ユーザーが自身の投票権をデリゲートする関数。

**詳細**
指定されたデリゲート先のアドレスに自身の投票権をデリゲートします。
デリゲート先のアドレスが`0x0`の場合、デリゲート先を呼び出し元（`msg.sender`）に設定します。

**引数**
- `delegatee`
	- デリゲート先のアドレス。

**戻り値**
なし

---

### delegateBySig

:::details delegateBySig
```solidity
function delegateBySig(
    address delegatee,
    uint256 nonce,
    uint256 expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
) public
```
:::

**概要**
署名を使用して投票権をデリゲートする関数。

**詳細**
署名を使用して投票権をデリゲートします。
署名が有効であること、`nonce`が正しいこと、有効期限内であることが確認されます。

**引数**
- `delegatee`
	- デリゲート先のアドレス。
- `nonce`
	- ノンス（トランザクション番号）。
- `expiry`
	- 署名の有効期限。
- `v`
	- 署名の回復バイト。
- `r`
	- ECDSA署名のr値。
- `s`
	- ECDSA署名のs値。

**戻り値**
なし

---

### getCurrentVotes

:::details getCurrentVotes
```solidity
function getCurrentVotes(address account) external view returns (uint96)
```
:::

**概要**
指定されたアカウントの現在の投票数を取得する関数。

**詳細**
指定されたアカウントの最新の投票チェックポイントを取得し、その中の投票数を返します。

**引数**
- `account`
	- 投票数を取得するアカウントのアドレス。

**戻り値**
- `uint96`
	- 指定されたアカウントの現在の投票数。

---

### getPriorVotes

:::details getPriorVotes
```solidity
function getPriorVotes(address account, uint256 blockNumber) public view returns (uint96)
```
:::

**概要**
特定のブロック番号におけるアカウントの過去の投票数を取得する関数。

**詳細**
指定されたブロック番号におけるアカウントの過去の投票チェックポイントを取得し、その中の投票数を返します。

**引数**
- `account`
	- 投票数を取得するアカウントのアドレス。
- `blockNumber`
	- ブロック番号。

**戻り値**
- `uint96`
	- 指定されたアカウントの指定されたブロック番号における過去の投票数。

---

### _delegate

:::details _delegate
```solidity
function _delegate(address delegator, address delegatee) internal
```
:::

**概要**
デリゲータが投票権をデリゲートするた関数。

**詳細**
デリゲータが指定されたデリゲート先に投票権をデリゲート（委任）します。
その際、現在のデリゲートとデリゲート先の投票数を更新します。

**引数**
- `delegator`
	- デリゲータのアドレス。
- `delegatee`
	- デリゲート先のアドレス。

**戻り値**
なし

---

### _moveDelegates

:::details _moveDelegates
```solidity
function _moveDelegates(
    address srcRep,
    address dstRep,
    uint96 amount
) internal
```
:::

**概要**
デリゲート先の変更に伴って投票数を移動する関数。

**詳細**
デリゲート先の変更に伴って、投票数の移動を行います。
送信元と送信先が異なる場合かつ移動する投票数が`0`より大きい場合に実行されます。

**引数**
- `srcRep`
	- 移動元のデリゲート先のアドレス。
- `dstRep`
	- 移動先のデリゲート先のアドレス。
- `amount`
	- 移動する投票数。

**戻り値**
なし

---

### _writeCheckpoint

:::details _writeCheckpoint
```solidity
function _writeCheckpoint(
    address delegatee,
    uint32 nCheckpoints,
    uint96 oldVotes,
    uint96 newVotes
) internal
```
:::

**概要**
投票数のチェックポイントを書き込む関数。

**詳細**
指定されたデリゲート先に対して投票数のチェックポイントを書き込みます。
チェックポイントがすでに存在する場合は更新し、存在しない場合は新たに作成します。

**引数**
- `delegatee`
	- デリゲート先のアドレス。
- `nCheckpoints`
	- チェックポイントの数。
- `oldVotes`
	- 更新前の投票数。
- `newVotes`
	- 更新後の投票数。

**戻り値**
なし

---

### safe32

:::details safe32
```solidity
function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32)
```
:::

**概要**
`uint256`型を`uint32`型に安全に変換する関数。

**詳細**
指定された`uint256`型の値を`uint32`型に変換します。
変換に失敗する場合、エラーメッセージを表示してリバートします。

**引数**
- `n`
	- 変換する値。
- `errorMessage`
	- エラーメッセージ。

**戻り値**
- `uint32`
	- 変換後の値。

---

### safe96

:::details safe96
```solidity
function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96)
```
:::

**概要**
`uint256`型を`uint96`型に安全に変換する関数。

**詳細**
指定された`uint256`型の値を`uint96`型に変換します。
変換に失敗する場合、エラーメッセージを表示してリバートします。

**引数**
- `n`
	- 変換する値。
- `errorMessage`
	- エラーメッセージ。

**戻り値**
- `uint96`
	- 変換後の値。

---

### add96

:::details add96
```solidity
function add96(
    uint96 a,
    uint96 b,
    string memory errorMessage
) internal pure returns (uint96)
```
:::

**概要**
`uint96`型の2つの数値を加算する関数。

**詳細**
指定された2つの`uint96`型の数値を加算します。
加算結果がオーバーフローする場合、エラーメッセージを表示してリバートします。

**引数 or パラメータ**
- `a`
	- 加算する値。
- `b`
	- 加算する値。
- `errorMessage`
	- エラーメッセージ。

**戻り値**
- `uint96`
	- 加算結果の値。

---

### sub96

:::details sub96
```solidity
function sub96(
    uint96 a,
    uint96 b,
    string memory errorMessage
) internal pure returns (uint96)
```
:::

**概要**
`uint96`型の2つの数値を減算する関数。

**詳細**
指定された2つの`uint96`型の数値を減算します。
減算結果がアンダーフローする場合、エラーメッセージを表示してリバートします。

**引数**
- `a`
	- 減算される値。
- `b`
	- 減算する値。
- `errorMessage`
	- エラーメッセージ。

**戻り値**
- `uint96`
	- 減算結果の値。

---

### getChainId

:::details getChainId
```solidity
function getChainId() internal view returns (uint256)
```
:::

**概要**
現在のチェーンのチェーンIDを取得関数。

**詳細**
アセンブリ命令を使用して現在のチェーンのチェーンIDを取得します。


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

---

# イベント

### Approval

:::details Approval
```solidity
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)
```
:::

**概要**
トークン所有者が特定のアドレスに対してトークンの承認をした時に発行されるイベント。

**パラメータ**
- `owner`
	- トークンの所有者のアドレス。
- `approved`
	- 承認されたアドレス。
- `tokenId`
	- 対象のトークンのID。

---

### ApprovalForAll

:::details ApprovalForAll
```solidity
event ApprovalForAll(address indexed owner, address indexed operator, bool approved)
```
:::

**概要**
トークン所有者が特定のアドレスに対してすべてのトークンの操作を許可または拒否した時に発行されるイベント。

**パラメータ**
- `owner`
	- トークンの所有者のアドレス。
- `operator`
	- 操作を行うアドレス。
- `approved`
	- 操作の許可（`true`）または拒否（`false`）。

---

### DelegateChanged

:::details DelegateChanged
```solidity
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate)
```
:::

**概要**
デリゲートが変更された時に発行されるイベント。

**パラメータ**
- `delegator`
	- デリゲートを変更するアドレス。
- `fromDelegate`
	- 変更前のデリゲート先のアドレス。
- `toDelegate`
	- 変更後のデリゲート先のアドレス。

---

### DelegateVotesChanged

:::details DelegateVotesChanged
```solidity
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance)
```
:::

**概要**
デリゲート先の投票数が変更された時に発行されるイベント。

**パラメータ**
- `delegate`
	- デリゲート先のアドレス。
- `previousBalance`
	- 変更前の投票数。
- `newBalance`
	- 変更後の投票数。

---

### DescriptorLocked

:::details DescriptorLocked
```solidity
event DescriptorLocked()
```
:::

**概要**
トークンURIのディスクリプタがロックされた時に発行されるイベント。

---

### DescriptorUpdated

:::details DescriptorUpdated
```solidity
event DescriptorUpdated(INounsDescriptor indexed descriptor)
```
:::

**概要**
トークンURIのディスクリプタが更新された時に発行されるイベント。

**パラメータ**
- `INounsDescriptor indexed descriptor`
	- 更新後のディスクリプタ。

---

### MinterLocked

:::details MinterLocked
```solidity
event MinterLocked()
```
:::

**概要**
トークンのミンターがロックされた時に発行されるイベント。

---

### MinterUpdated

:::details MinterUpdated
```solidity
event MinterUpdated(address indexed minter)
```
:::

**概要**
トークンの`minter`が更新された時に発行されるイベント。

**パラメータ**
- `minter`
	- 新しい`minter`のアドレス。

---

### NounBurned

:::details NounBurned
```solidity
event NounBurned(uint256 indexed tokenId)
```
:::

**概要**
トークンが焼却された時に発行されるイベント。

**パラメータ**
- `tokenId`
	- 焼却されたトークンのID。

---

### NounCreated

:::details NounCreated
```solidity
event NounCreated(uint256 indexed tokenId, INounsSeeder.Seed seed)
```
:::

**概要**
新しいトークンが生成された時に発行されるイベント。

**パラメータ**
- `tokenId`
	- 生成されたトークンのID。
- `INounsSeeder.Seed seed`
	- 生成に使用されたシード情報。

---

### NoundersDAOUpdated

:::details NoundersDAOUpdated
```solidity
event NoundersDAOUpdated(address indexed noundersDAO)
```
:::

**概要**
Nounders DAO（デリゲート先）が更新された時に発行されるイベント。


**パラメータ**
- `noundersDAO`
	- 新しいNounders DAO（デリゲート先）のアドレス。

---

### OwnershipTransferred

:::details OwnershipTransferred
```solidity
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
```
:::

**概要**
トークンのオーナーシップが移転された時に発行されるイベント。

**パラメータ**
- `previousOwner`
	- 移転前のオーナーのアドレス。
- `newOwner`
	- 新しいオーナーのアドレス。

---

### SeederLocked

:::details SeederLocked
```solidity
event SeederLocked()
```
:::

**概要**
トークンのシーダーがロックされた時に発行されるイベント。

---

### SeederUpdated

:::details SeederUpdated
```solidity
event SeederUpdated(INounsSeeder indexed seeder)
```
:::

**概要**
トークンのシーダーが更新された時に発行されるイベント。

**パラメータ**
- `INounsSeeder indexed seeder`
	- 新しいシーダーのアドレス。

---

### Transfer

:::details Transfer
```solidity
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)
```
:::

**概要**
トークンの所有権が移転された時に発行されるイベント。

**パラメータ**
- `from`
	- 移転元のアドレス。
- `to`
	- 移転先のアドレス。
- `tokenId`
	- 移転されたトークンのID。

---

# コード

## ベースコントラクト

- `contracts/base`

## インターフェース

- `/contracts/interfaces`

## 外部コントラクト

- `/contracts/external`

# 最後に

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

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

https://chaldene.net/

https://qiita.com/cardene
