# [Bunzz Decipher] BAYC (BoredApeYachtClub)のコントラクトを見てみよう！

> BAYCは、 NFTであるERC721トークンの標準規格であり、Ethereumブロックチェーン上に構築されたコントラクトです。

- URL: https://cardene.dev/articles/dab1107e0f9e0f
- 公開日: 2023-07-14
- 更新日: 2023-07-14
- トピック: Blockchain
- タグ: solidity, ethereum, nft, smartcontract

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回はBunzzの新機能『**DeCipher**』を使用して、BAYCのコントラクトを見てみようと思います。
『**DeCipher**』はAIを使用してコントラクトのドキュメントを自動生成してくれるサービスです。

:::message alert
『**DeCipher**』は現在クローズドβ版になります。
:::

BAYCのコントラクトは以下になります。
https://etherscan.io/token/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d#code

生成されたドキュメントを翻訳・要約・補足しながらまとめていきます。

# 概要

BAYCは、 NFTであるERC721トークンの標準規格であり、Ethereumブロックチェーン上に構築されたコントラクトです。
NFTは、スポーツカードから仮想不動産、デジタルスニーカーに至るまで、さまざまなユニークな有形無形のアイテムを表すデジタル資産です。

BAYCの場合、各トークンはユニークなデジタルコレクション「Bored Ape」を表し、それぞれが異なる特性を持っています。
BAYCコントラクトは、これらのユニークなデジタルエイプのMint、所有権、販売に関するルールを規定しています。

## コントラクトの機能
BAYCコントラクトでは、NFTの送付、交換、または販売方法を定義しています。
所有者専用の関数、Mint関数、販売と価格に関する詳細、および証明に関連する情報が含まれます。

### BoredApeYachtClub()
コントラクトがデプロイされると、`BoredApeYachtClub()`コントラクト内の`constructor`が呼び出されます。
`constructor`は、コントラクトがデプロイされた時に一度実行される関数です。
ここでは、NFTの総供給量（`maxNftSupply`）と、販売が開始されるタイムスタンプ（`saleStart`）の設定をしています。

```solidity
constructor(string memory name, string memory symbol, uint256 maxNftSupply, uint256 saleStart) ERC721(name, symbol) {
	MAX_APES = maxNftSupply;
	REVEAL_TIMESTAMP = saleStart + (86400 * 9);
}
```

### reserveApes()
`reserveApes()`関数は、コントラクトの所有者がNFTの数を予約することを可能にします。
この関数は新しいBAYC NFTをMintし、それらを所有者のアドレスに割り当てます。
所有者は、この関数が呼び出されるたびに最大30匹のエイプを一度に予約することができます。

```solidity
function reserveApes() public onlyOwner {        
	uint supply = totalSupply();
	uint i;
	for (i = 0; i < 30; i++) {
	    _safeMint(msg.sender, supply + i);
	}
}
```

### withdraw()
`withdraw()`関数は、コントラクトの所有者がコントラクトの残高を引き出すことを可能にします。
これにより、NFTの販売から得られたETHが所有者のアドレスに送付されます。

```solidity
function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        msg.sender.transfer(balance);
}
```

### setProvenanceHash
`setProvenanceHash()`関数は、BoredApesが最初に生成されたものと同じであることを証明するハッシュを設定します。
ハッシュは一意であり、各NFTのごとに定義されています。
これにより、所有権と所有権の移転が時間を経て確立されます。

```solidity
function setProvenanceHash(string memory provenanceHash) public onlyOwner {
        BAYC_PROVENANCE = provenanceHash;
}
```

**参考記事**
https://dev.to/brodan/learning-nft-provenance-by-example-a-bored-ape-investigation-hfe

## その他の機能

### 販売と価格の詳細
コントラクトでは、各NFTの価格（`apePrice`を`0.08 ETH`に設定）、単一トランザクションでのユーザーが購入できる最大エイプ数（`maxApePurchase`を`20`に設定）、および販売がアクティブかどうかを示す真偽値（`saleIsActive`）も指定されています。

### 所有権のTransfer
コントラクトの所有者は、所有権を別のEthereumアドレスに移動するために、以下の関数を使用することもできます。
- `transferOwnership()`
- `renounceOwnership()`

#### transferOwnership()
現在のコントラクトの所有者がコントラクト操作の権限を別のアドレスに移す。

#### renounceOwnership()
現在のコントラクトの所有者が所有権を完全に放棄し、所有者のアドレスをゼロに設定する。

# 使い方

BoredApeYachtClubコントラクトは、Ethereumブロックチェーンのコントラクトの1種類である、ERC721規格に沿ってNFTの作成、所有、送付、販売を可能にします。

このコントラクトを利用するには、以下の手順に従う必要があります。

## コントラクトのデプロイ

コントラクトはまずデプロイする必要があります。
公開ネットワーク（Ethereum MainnetやRopstenなど）やプライベートネットワークでデプロイできます。
コンストラクタ関数はデプロイ時に一度だけ実行されます。
ユニークなNFTの名前とシンボル、NFTの最大供給量、販売開始時間を設定しています。

## NFTの設定
`reserveApes`関数は、一部のNFTのMintを予約するために使用されます。
この関数はコントラクトの所有者だけが実行できます。

## 所有権の管理
`transferOwnership`関数は契約の所有権を新しいアカウントに移動します。
すべての機能を放棄するために、所有者は`renounceOwnership`関数を使用してすべての所有権特権を放棄することもできます。

## 販売と送付の実装

`maxApePurchase`関数は、買うことができるエイプの最大数に制限を設けます。
`mintApe`関数は購入したいNFTの数を引数でとり、NFTの購入を可能にします。

## 資金の管理

契約の所有者は、いつでも`withdraw`関数を実行してコントラクト内のすべての資金を引き出すことができます。

## リビールとNFTの証明

`REVEAL_TIMESTAMP`は、NFTの所有者が自分のNFTの中身を見ることができる開始時間です。
`BAYC_PROVENANCE`は、BoredApesが最初に生成されたものと同じであることを証明するハッシュを記録します。

:::message
コントラクトの所有者のみが、`withdraw`、`reserveApes`、`transferOwnership`、`renounceOwnership`のような所有者専用の関数を実行できる。
:::

# モジュールのパラメータ

コントラクトをデプロイする時に一度だけ実行される、`constructor`の引数の説明がされています。

- `name`
	- NFTの名前。
- `symbol`
	- NFTのシンボル名。
- `maxNftSupply`
	- NFTの最大供給量。
- `saleStart`
	- NFTの販売が開始される時間のタイムスタンプ。


# 関数

前章まででコアな関数については説明してきました。
この章では前章までで取り上げられなかった、BAYC特有の関数について抜粋していきます。
また、ERC721規格内の関数については以下の記事などを参考にしてください。

https://chaldene.net/erc721

## Write系

### flipSaleState

NFTを販売期間内か期間外か設定する関数。
`saleIsActive`変数を現在の値から反対の値に変更します。

NFTの所有者は、NFTの販売状態を切り替えたいときに呼び出します。
例）`saleIsActive`が`true`で、ユーザが販売を一時停止したい場合、この関数を呼び出して`saleIsActive`を`false`に設定できます。

この関数に戻り値はなく、`saleIsActive`変数を直接更新します。

:::message
この関数は、コントラクトに実装されているアクセス制御によって、コントラクトの所有者または許可されたオペレータによってのみ呼び出すことができます。
:::

```solidity
function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
}
```

### setStartingIndex
この関数は、NFTの開始インデックスを設定する関数。
開始インデックスは、NFTを生成してユーザーに割り当てる順番を決定します。

この関数を呼び出す前に、以下の条件を満たす必要があります。

- コントラクト所有者のみが呼び出すことができる。
- 販売開始前や何らかのブロック中など、NFTがMintされない状態になければならない。

```solidity
function setStartingIndex() public {
        require(startingIndex == 0, "Starting index is already set");
        require(startingIndexBlock != 0, "Starting index block must be set");
        
        startingIndex = uint(blockhash(startingIndexBlock)) % MAX_APES;
        // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
        if (block.number.sub(startingIndexBlock) > 255) {
            startingIndex = uint(blockhash(block.number - 1)) % MAX_APES;
        }
        // Prevent default sequence
        if (startingIndex == 0) {
            startingIndex = startingIndex.add(1);
        }
}
```

### emergencySetStartingIndexBlock

不測の事態や問題によって開始インデックスブロックを調整する必要が生じた場合に、開始インデックスブロックを手動で設定する関数。
コントラクトの所有者のみが実行でき、コントラクトの機能や動作に影響を与える可能性があるため、注意して使用する必要があります。

```solidity
function emergencySetStartingIndexBlock() public onlyOwner {
        require(startingIndex == 0, "Starting index is already set");
        
        startingIndexBlock = block.number;
}
```

## Read系

### startingIndex
NFTの現在の開始インデックスを定義している変数。

```solidity
uint256 public startingIndex;
```

### startingIndexBlock

`tokenByIndex`と`tokenOfOwnerByIndex`関数の開始インデックスが決定される、イーサリアムのブロック番号を表す変数。
ユーザーが現在の開始インデックスブロック番号を取得するために使用します。
ユーザーはこの値を使用して、特定の所有者のトークンを反復処理する開始ブロック番号、またはインデックスを決定することができます。
開始インデックスのブロック番号はコントラクトのオーナーが設定し、ユーザーはトークンの正しいインデックスにアクセスしていることを確認するために、この値を取得する必要があります。

```solidity
uint256 public startingIndexBlock;
```

### tokenOfOwnerByIndex

コレクション内のNFTのインデックスと指定されたオーナーが所有する、特定のNFTのtokenIdを返す関数。
owner（address型）とindex（uint256型）の2つのパラメータを受け取ります。

```solidity
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        return _holderTokens[owner].at(index);
}
```
    

# Event
このタブではEventについて説明されています。

## OwnershipTransferred
コントラクトの前の所有者と新しい所有者の情報をオンチェーンにログとして残します。
コントラクトの所有権があるアドレスから別のアドレスに移ったときに発行されます。
このイベントには以下の2つのパラメータがあります。

- `previousOwner`
	- 契約の前の所有者のアドレス。
- `newOwner`
	- 契約の新しい所有者のアドレス。

## Approval

特定のNFTの転送許可されたアドレスの承認ステータスが変更されるたびに発行されます。

以下の3つのパラメータがあります。

- `owner`
	- トークン所有者のアドレス。
- `approved`
	- NFTの転送が承認されたアドレス。
- `tokenId`
	- 承認ステータスが変更されたNFTの一意な識別子。

## ApprovalForAll
特定のアドレスが所有している全てのNFTを別のアドレスに送付許可が変更された時に発行されます。

以下の3つのパラメータがあります。

- `owner`
	- トークン所有者のアドレス。
- `operator`
	- NFTの転送が承認されたアドレス。
- `approved`
	- 承認されたか、承認が外されたかの真偽値。

# コード

『**DeCipher**』の以下の部分にもコードが置かれています。

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

# 最後に

今回の記事では、Bunzzの新機能『**DeCipher**』を使用して、BAYCのコントラクトを見てきました。
生成されるドキュメントは想像していたものより細かく説明されていて驚きました。
ただ、関数単位で細かい実装を理解するのには多少の補足が必要だったりしますが、ドキュメントとしては十分だと思います！
ただ、ドキュメントを読んでからコントラクトの中身を見ることで、理解の効率がぐんとあがりました。
非エンジニアの方はSolidityやNFTに対しての理解が必要ですが、エンジニアの方であればめちゃくちゃ便利なのではないでしょうか？
今後も特定のNFTやコントラクトをピックアップしてまとめてみたいと思います。

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

https://chaldene.net/

https://qiita.com/cardene
