[ERC7564] コントラクトウォレットでNFTを管理し特有の関数を追加する仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、コントラクトウォレット内のNFT管理に重点を置き、処理の柔軟性とセキュリティを強化する仕組みを提案している**ERC7564**についてまとめていきます！

:::note info
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-7564

他にも様々なEIPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

# 概要

この提案は、NFT（Non-Fungible Tokens、非代替トークン）の管理にスマートコントラクトウォレットを使用する新しいアプローチを紹介しています。
スマートコントラクトウォレットのプログラム可能な特性を活用して、NFT資産をより柔軟かつ安全、効率的に管理する方法を提供します。
ここで紹介されている機能は以下の通りです。

- **nftApprove**
    - 特定のNFTを特定のアドレスに対して承認（`approve`）します。
    - これにより、そのアドレスはNFTを代わりに移動することができます。
- **nftSetApprovalForOneAll**
    - 特定のNFT集合（例えば、あるコレクション内の全てのNFT）に対して、1つのアドレスを承認（`approve`）します。
- **nftSetApprovalForAllAll**
    - 複数のNFT集合に対して、1つのアドレスを承認（`approve`）します。
    - これは、異なるコレクションのNFTを一括で扱う場合に便利です。
- **nftGetApproved**
    - 特定のNFTに対して現在承認されているアドレスを取得します。
- **nftIsApprovedForOneAll**
    - 特定のNFT集合に対して、あるアドレスが承認（`approve`）されているかどうかを確認します。
- **nftIsApprovedForAllAll**
    - 複数のNFT集合に対して、あるアドレスが承認（`approve`）されているかどうかを確認します。
- **nftTransfer**
    - 承認（`approve`）されたアドレスがNFTを別のアドレスに移動させる機能です。

これらの機能を利用することで、NFTの所有権やアクセス権をより詳細に管理することが可能になります。
従来のウォレットでは、NFTを移動させるためには所有者が直接行動を起こさなければなりませんでしたが、スマートコントラクトウォレットを使用することで、特定の条件下での自動承認や一括承認が可能になり、NFTの取引や管理をより柔軟に行えるようになります。

このアプローチの大きな利点は、セキュリティと効率性の向上です。
スマートコントラクトにより事前に定義されたルールに基づいて操作が行われるため、誤ってNFTを失うリスクを減らし、管理作業を自動化することで手間を大幅に削減できます。

# 動機

この提案は、スマートコントラクトウォレットを使ってNFT（非代替トークン）をより柔軟に管理する方法に関するものです。
従来の外部所有アカウント（EOA）ウォレットとは異なり、スマートコントラクトウォレットには状態（`state`）とコード（`code`）の格納スペースがあります。
これにより、ウォレット自体にプログラム可能な機能を持たせることができます。

:::note info
`state`とはデータのことです。
:::

### アカウントの抽象化（Account Abstraction, AA）

アカウントの抽象化は、スマートコントラクトウォレットが取り組んでいる方向性の1つです。
これは、抽象的なアカウントを扱うことで、より柔軟なウォレットの設計を可能にします。
**ERC4337**に基づいた拡張や、ウォレットのプラグインとして機能することも想定されています。

:::note info
**ERC4337**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/8c97ca7c93c4557ebfc2

## このERCの目的

### NFT資産のウォレット自体による管理
この提案では、NFT資産をウォレット自体で割り当て、管理することを目指しています。
これにより、ユーザーのコントラクトウォレットによって設定された承認機能などが可能になり、既存の**ERC721**コントラクトのリスクを避けることができます。

:::note info
**ERC721**については以下の記事を参考にしてください。
:::

https://chaldene.net/erc721

### nftTransfer関数の追加
スマートコントラクトウォレット自体によるトランザクションの開始。

### 承認と`transfer`機能の拡張
`nftApprove`, `nftSetApprovalForOneAll`, `nftSetApprovalForAllAll`, `nftGetApproved`, `nftIsApprovedForOneAll`, `nftIsApprovedForAllAll`などの関数を追加し、バッチ承認やバッチトランスファーを可能にします。

### フック機能のカスタマイズ
`transfer`前後にフック関数を追加することで、より柔軟な実装が可能になります。

### `nftReceive`関数の実装
ユーザーが自由に実装できるようにします。

## 対応するNFT規格

この提案は、SimpleNFT（ERC-X）と呼ばれる新しいNFT規格をサポートし、既存の**ERC721**規格との後方互換性を保持します。
これにより、市場に存在する全てのNFTの管理が可能になります。

:::note info
この提案はNFTの管理とトランザクションをより安全で、柔軟かつ効率的に行うための新しい方法を提案しています。
ユーザーは自身のスマートコントラクトウォレットを通じて、NFT資産に対するより細かいコントロールを持つことができるようになります。
:::

# 仕様

この規格に準拠する契約は、**ERC165**インターフェイスを実装する必要があります。

:::note info
**ERC165**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/541184977a5cba51b759

```ts
/// @title ERC-7564
/// @dev See https://eips.ethereum.org/EIPS/eip-7564
/// @dev Note: the ERC-165 identifier for this interface is 
pragma solidity ^0.8.20;

interface IERC7564{

    /**
     * @notice Used to notify listeners that owner has granted approval to the user to manage one nft.
     * @param _asset Address of the nft
     * @param _owner Address of the account that has granted the approval for nft‘s assets
     * @param _operator Address of the operator
     * @param _tokenId The unique identifier of the NFT
     */
    event NftApproval(
        address indexed _asset,
        address indexed _owner, 
        address indexed _operator, 
        uint256 _tokenId
    );

    /**
     * @notice Used to notify listeners that owner has granted approval to the operator to manage all nft of one asset contract.
     * @param _asset Address of the nft
     * @param _owner Address of the account that has granted the approval for nft‘s assets
     * @param _operator Address of the operator
     * @param _approved approve all nft of one asset contract
     */
    event NftApprovalForOneAll(
        address indexed _asset,
        address indexed _owner, 
        address indexed _operator,
        bool _approved
    );

    /**
     * @notice Used to notify listeners that owner has granted approval to the operator to manage all nft .
     * @param _owner Address of the account that has granted the approval for nft‘s assets
     * @param _operator Address of the operator
     * @param _approved approve all nft
     */
    event NftApprovalForAllAll(
        address indexed _owner, 
        address indexed _operator,
        bool _approved
    );

    /**
     * @notice Approve nft
     * @dev Allows operator address to withdraw from your wallet one nft.
     * @dev Emits an {nftApproval} event.
     * @param _asset Address of the nft
     * @param _operator Address of the operator
     * @param _tokenId The unique identifier of the NFT
     */
    function nftApprove(address _asset, address _operator, uint256 _tokenId) external;

   

    /**
     * @notice Approve all nft of one asset
     * @dev Allows operator address to withdraw from your wallet all nft.
     * @dev Emits an {nftApprovalForOneAll} event.
    * @param _asset Address of the nft
     * @param _operator Address of the operator
     * @param _approved Approved all nfts of one asset
     */
    function nftSetApprovalForOneAll(address _asset, address _operator, bool _approved) external;


     /**
     * @notice Approve all nft
     * @dev Allows operator address to withdraw from your wallet all nft.
     * @dev Emits an {nftApprovalForAllAll} event.
     * @param _operator Address of the operator
     * @param _approved Approved all nfts
     */
    function nftSetApprovalForAllAll(address _operator, bool _approved) external;

    /**
     * @notice read operator approved
     * @param _asset Address of the nft
     * @param _operator Address of the operator
     * @param _tokenId The unique identifier of the NFT
     * @return _approved Whether to approved operator one nft
     */
    function nftGetApproved(address _asset, address _operator, uint256 _tokenId) 
        external
        view
        returns (bool _approved);

    /**
     * @notice read operator approved
     * @param _asset Address of the nft
     * @param _operator Address of the operator
     * @return _approved Whether to approved operator all nfts of this one asset
     */
    function nftIsApprovedForOneAll(address _asset, address _operator) 
        external
        view
        returns (bool _approved);

    /**
     * @notice read operator approved
     * @param _operator Address of the operator
     * @return _approved Whether to approved operator all nfts
     */
    function nftIsApprovedForAllAll(address _operator) 
        external
        view
        returns (bool _approved);

    /**
     * @notice Transfer nft
     * @dev must call nft asset transfer() inside the function
     * @dev If the caller is not wallet self, must verify the approve and update
     * @param _asset Address of the nft
     * @param _to Address of the receive
     * @param _tokenId The transaction amount
     * @return _success The bool value returns whether the transfer is successful
     */
    function nftTransfer(address _asset, address _to, uint256 _tokenId) 
        external 
        returns (bool _success); 


}
```

---

### NftApproval

```javascript
event NftApproval(
    address indexed _asset,
    address indexed _owner, 
    address indexed _operator, 
    uint256 _tokenId
);
```

**概要**

NFTの所有者が、特定のユーザーに1つのNFTを管理するための承認（`approve`）を与えたことを通知するイベント。

**詳細**

このイベントは、NFTの所有者がオペレーターに対して、特定のNFTの管理を許可した時に発行されます。
これにより、承認（`approve`）されたオペレーターは、指定されたNFTに対して特定の操作を行うことができるようになります。

**パラメータ**

- `_asset`
    - NFTのアドレス。
- `_owner`
    - NFTの資産の承認（`approve`）を与えたアカウントのアドレス。
- `_operator`
    - オペレーターのアドレス。
- `_tokenId`
    - NFTの一意識別子。

---

### NftApprovalForOneAll

```javascript
event NftApprovalForOneAll(
    address indexed _asset,
    address indexed _owner, 
    address indexed _operator,
    bool _approved
);
```

**概要**

所有者がオペレーターに、あるアセットコントラクトの全てのNFTを管理するための承認（`approve`）を与えたことを通知するイベント。

**詳細**

このイベントは、NFTの所有者がオペレーターに対して、あるアセットコントラクトに属する全てのNFTの管理を許可した時に発行されます。
所有者がオペレーターに、あるアセットコントラクトの全てのNFTを管理するための承認（`approve`）を与えたことをリスナーに通知します。
これにより、承認されたオペレーターは、そのアセットコントラクトに属する全てのNFTに対して特定の操作を行うことができます。

**パラメータ**

- `_asset`
    - NFTのアドレス。
- `_owner`
    - NFTの資産の承認（`approve`）を与えたアカウントのアドレス。
- `_operator`
    - オペレーターのアドレス。
- `_approved`
    - あるアセットコントラクトの全てのNFTに対する承認（`approve`）状態。

---

### NftApprovalForAllAll

```javascript
event NftApprovalForAllAll(
    address indexed _owner, 
    address indexed _operator,
    bool _approved
);
```

**概要**

所有者がオペレーターに全てのNFTを管理するための承認を与えたことを通知するイベント。

**詳細**

このイベントは、NFTの所有者がオペレーターに対して全てのNFTの管理を許可した時に発行されます。
これにより、承認（`approve`）されたオペレーターは、市場に存在する全てのNFTに対して特定の操作を行うことができるようになります。

**パラメータ**

- `_owner`
    - NFTの資産の承認を与えたアカウントのアドレス。
- `_operator`
    - オペレーターのアドレス。
- `_approved`
    - 全てのNFTに対する承認（`approve`）状態。

---

### `nftApprove`

```javascript
function nftApprove(address _asset, address _operator, uint256 _tokenId) external;
```

**概要**

特定のNFTを特定のアドレスに承認（`approve`）する関数。

**詳細**

この関数は、オペレーターアドレスがあなたのウォレットから1つのNFTを引き出すことを許可します。
また、`nftApproval`イベントを発行します。

**引数**

- `_asset`
	- NFTのアドレス。
- `_operator`
	- オペレーターのアドレス。
- `_tokenId`
	- NFTのユニークな識別子。

---

### `nftSetApprovalForOneAll`

```javascript
function nftSetApprovalForOneAll(address _asset, address _operator, bool _approved) external;
```

**概要**

1つの資産に含まれる全NFTを承認する関数。

**詳細**

この関数は、オペレーターアドレスがあなたのウォレットから特定の資産に属する全NFTを引き出すことを許可します。
`nftApprovalForOneAll`イベントを発行します。

**引数**

- `_asset`
	- NFTのアドレス。
- `_operator`
	- オペレーターのアドレス。
- `_approved`
	- 一つの資産の全NFTを承認するかどうかの`bool`値。

---

### `nftSetApprovalForAllAll`

```javascript
function nftSetApprovalForAllAll(address _operator, bool _approved) external;
```

**概要**

全てのNFTを承認する関数。

**詳細**

この関数は、オペレーターアドレスがあなたのウォレットから全NFTを引き出すことを許可します。
`nftApprovalForAllAll`イベントを発行します。

**引数**

- `_operator`
	- オペレーターのアドレス。
- `_approved`
	- 全NFTを承認するかどうかの真偽値。

---

### `nftGetApproved`

```javascript
function nftGetApproved(address _asset, address _operator, uint256 _tokenId) external view returns (bool _approved);
```

**概要**

特定のオペレーターが特定のNFTを承認されているか確認する関数。

**詳細**

この関数は、指定されたNFTに対して特定のオペレーターが承認されているかどうかを返します。

**引数**

- `_asset`
	- NFTのアドレス。
- `_operator`
	- オペレーターのアドレス。
- `_tokenId`
	- NFTのユニークな識別子。

**戻り値**

- `_approved`
	- 特定のオペレーターが特定のNFTを承認されているかどうかの`bool`値。

---

### `nftIsApprovedForOneAll`

```javascript
function nftIsApprovedForOneAll(address _asset, address _operator) external view returns (bool _approved);
```

**概要**

特定の資産に含まれる全NFTが特定のオペレーターによって承認されているか確認する関数。

**詳細**

この関数は、あるNFT資産に対して、特定のオペレーターが全てのNFTの操作を許可されているかどうかを返します。
これにより、一括操作の承認状態を簡単に確認できます。

**引数**

- `_asset`
	- NFTのアドレス。
- `_operator`
	- オペレーターのアドレス。

**戻り値**

- `_approved`
	- 特定の資産の全NFTが特定のオペレーターに承認されているかどうかの`bool`値。

---

### `nftIsApprovedForAllAll`

```javascript
function nftIsApprovedForAllAll(address _operator) external view returns (bool _approved);
```

**概要**

全てのNFTが特定のオペレーターによって承認されているか確認する関数。

**詳細**

この関数は、ユーザーのウォレット内の全てのNFTに対して、特定のオペレーターが操作を許可されているかどうかを確認します。
これは、複数の資産にわたる一括操作の承認状態を管理するのに役立ちます。

**引数**

- `_operator`
	- オペレーターのアドレス。

**戻り値**

- `_approved`
	- 全てのNFTが特定のオペレーターに承認されているかどうかの`bool`値。

---

### `nftTransfer`

```javascript
function nftTransfer(address _asset, address _to, uint256 _tokenId) external returns (bool _success);
```

**概要**

NFTを`transfer`する関数。

**詳細**

この関数は、指定されたNFTを別のアドレスに送付します。
呼び出し元がウォレット自体でない場合は、承認（`approve`）を検証して更新する必要があります。
送付の成功を`bool`値で返します。

**引数**

- `_asset`
	- NFTのアドレス。
- `_to`
	- 受け取りアドレス。
- `_tokenId`
	- トランザクションの量（NFTのID）。

**戻り値**

- `_success`
	- 転送が成功したかどうかの`bool`値。

---

# 補足

この提案の鍵となる技術的決定は、改良された承認（`approve`）メカニズム、最適化された`transfer`プロセス、およびバッチ操作のサポートの3つの主要な点に集中しています。
これらの決定は、NFT（非代替トークン）の管理と操作を効率化し、セキュリティを強化することを目指しています。

## 改良された承認メカニズム

### 現行対提案
現在の**ERC721**システムでは、外部所有アカウント（EOA）が直接NFTコントラクトと対話して承認を行います。
この提案に含まれる`nftApprove`、`nftSetApprovalForOneAll`、`nftSetApprovalForAllAll`、`nftGetApproved`、`nftIsApprovedForOneAll`、`nftIsApprovedForAllAll`関数は、ウォレットコントラクト内のNFT使用に対するより精密な制御を可能にし、従来の方法を大きく改善します。

### 強化されたセキュリティ
このメカニズムは、ユーザーのスマートコントラクトウォレットに承認制御を移行することで、NFTの過度な承認などのリスクを軽減します。

### プログラム可能性
ユーザーは、条件付き承認や期限付き承認など、従来の**ERC721** NFTでは不可能だった高度な承認戦略を設定する能力を得ます。
`nftSetApprovalForAllAll`関数は特に、全NFTに対するユニバーサルな設定を可能にします。

## 最適化された転送プロセス

### 効率とセキュリティ
`nftTransfer`関数はNFTの`transfer`プロセスを合理化し、トランザクションをより効率的かつ安全にします。

### 柔軟性
`transfer`前後にカスタムロジック（フック）を統合することを可能にし、ユーザーのニーズに合わせた追加のセキュリティチェックや特定のアクションを実現します。

## バッチ操作のサポート

### 増加した効率
ユーザーは複数の承認または`transfer`操作を同時に処理することができ、トランザクションの効率を大幅に向上させます。

### 向上したユーザー体験
多数の資産の管理を簡素化し、大きなポートフォリオを持つユーザーの全体的な体験を改善します。

:::note info
この提案はNFTの管理と利用における柔軟性、セキュリティ、および効率性を大きく向上させることを目的としています。
:::

# 互換性

このERCは**ERC4337**の拡張として使用でき、**ERC4337**と後方互換性があります。

# 引用

Xiang (@wenzhenxiang), Ben77 (@ben2077), Mingshi S. (@newnewsms), "ERC-7564: Contract wallet management NFT [DRAFT]," Ethereum Improvement Proposals, no. 7564, November 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7564.

# 最後に

今回は「コントラクトウォレット内のNFT管理に重点を置き、処理の柔軟性とセキュリティを強化する仕組みを提案している**ERC7564**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4