[ERC7561] コントラクトウォレット用に設計された新たなERC721規格の仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC721**から`safeTransferFrom`、`approve`、`setApprovalForAll`、`getApproved`、`isApprovedForAll`関数を削除し、コントラクトウォレット用に設計された**ERC7561**についてまとめていきます！

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

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

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

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

# 概要

新しいタイプのNFT(非代替トークン)アセットに関する提案です。
このNFTアセットは、「**ユーザーコントラクトウォレット（アカウント抽象化を含む）**」に基づいて設計されており、**ERC721**との互換性を持ちながらも、よりシンプルな形で提供されています。

### ユーザーコントラクトウォレット（アカウント抽象化）

**ユーザーコントラクトウォレット**は、従来のウォレット（アカウント）とは異なり、より複雑な操作や設定が可能なスマートコントラクトに基づいたウォレットです。
**アカウント抽象化**とは、アカウントの詳細（例：アドレスやキー管理）を抽象化し、ユーザーにとってより使いやすい形にする技術です。
これにより、例えばウォレットのセキュリティ強化や複数の署名要求など、より高度な機能を実装できます。

:::note info
**アカウント抽象化**については以下の記事を参考にしてください。
:::

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

### ERC721との互換性

**ERC721**は、NFTを扱うための標準規格の一つで、個々のトークンが一意の識別子を持ち、それぞれ異なる属性を持つことができます。
この規格により、アート作品やコレクタブルアイテムなどをデジタルで一意に表現することが可能になります。

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

https://chaldene.net/erc721

### シンプルな設計

提案された新しいERCは、ERC-721のいくつかの機能を削除することで、NFTアセットをよりシンプルに保つことを目指しています。具体的には、以下の機能が削除されています：

- **approve**：他のアドレスが特定のトークンを譲渡できるように許可します。
- **setApprovalForAll**：他のアドレスが所有する全てのトークンを譲渡できるように許可します。
- **getApproved**：特定のトークンを譲渡する権利があるアドレスを返します。
- **isApprovedForAll**：あるアドレスが他のアドレスの全てのトークンを譲渡する権利を持っているかどうかを確認します。
- **safeTransferFrom**：トークンの安全な譲渡を行います。

これらの機能を削除することで、新しいERCは操作を簡単にし、開発者がNFTアセットをより容易に扱えるようにすることを目指しています。ただし、基本的なNFTの特性や**ERC721**との互換性は維持されています。

:::note info
イーサリアムには主に外部所有アカウント（Externally Owned Accounts, EOA）とコントラクトアカウントという、二つのタイプのアカウントが存在します。
これらのアカウントは、イーサリアムブロックチェーン上での相互作用やトランザクションの基本的な要素となります。

### 外部所有アカウント（EOA）

**EOA**は、個人や団体によって直接管理されるアカウントです。
プライベートキーによって制御され、このキーを持っている人だけがそのアカウントを操作できます。
EOAからのトランザクションは、プライベートキーによって署名され、イーサを送ったり、スマートコントラクトを実行するために使用されます。
EOAは自身のコードを持たず、状態（イーサの残高など）のみを持ちます。
すなわち、自動的にトランザクションを発行したり、条件に基づいて動作することはできません。

### コントラクトアカウント

**コントラクトアカウント**は、スマートコントラクトのコードによって管理されるアカウントです。
このコードはイーサリアムブロックチェーン上にデプロイされ、トランザクションを受信すると、そのコードに従って自動的に実行されます。
コントラクトアカウントは、条件付きのトランザクション、分散型アプリケーション（DApps）のロジック、あるいはより複雑なコントラクトの実行など、多様な機能を持つことができます。
コントラクトアカウントもまた状態を持ちますが、EOAとは異なり、プライベートキーによって制御されるのではなく、そのコントラクトのコードによって行動が決定されます。

### EOAとコントラクトアカウントの主な違い

#### 管理方法
EOAはプライベートキーによって人間により直接管理されますが、コントラクトアカウントはスマートコントラクトのコードによって自動的に操作されます。

#### 機能性
EOAはイーサの送受信やスマートコントラクトの起動が可能ですが、自身から自動的に動作することはありません。
一方、コントラクトアカウントはトランザクションを受け取ると、あらかじめプログラムされた条件に従って自動的に動作します。

#### 用途
EOAは基本的なトランザクションや資金の保管に使用され、コントラクトアカウントは分散型アプリケーションのバックエンドロジックや自動化された契約など、より複雑な操作に使用されます。

このように、イーサリアムの二つのアカウントタイプは、ブロックチェーン上で異なる役割と機能を果たします。
:::

# 動機

この提案は、NFT（非代替トークン）に関する既存の標準である**ERC721**の考え方を新しくします。
まず、**ERC721**の基本について理解し、その後でこの新しい提案がどのように異なるのかを見ていきましょう。

## ERC721の基本

**ERC721**は、イーサリアムブロックチェーン上でユニークなアセットを表すための標準です。
各NFTは一意のIDで識別され、異なる特性を持つことができます。
**ERC721**では、トークンの移転や所有権の変更に関連する関数（`transferFrom`, `approve`, `getApproved`, `setApprovalForAll`, `isApprovedForAll`, `safeTransferFrom`）が定義されています。

## 新提案の概要

この新提案は、NFTのコントラクトをシンプルに保ちつつ、スマートコントラクトウォレットを基盤として設計することを目指しています。
このアプローチにより、以下の目標を達成することを目指しています。

### NFTコントラクトのシンプル化
NFTコントラクトは`transferFrom`関数のみを担当し、他のすべての機能は削除されます。
これにより、NFTコントラクトの複雑さが大幅に減少します。

### 権限管理のユーザーレベルへの移行
`approve`, `getApproved`, `setApprovalForAll`, `isApprovedForAll`などの機能は、NFTコントラクトではなく、ユーザーレベルで管理されます。
これにより、ユーザーは自身のアセットに対するより大きな柔軟性と制御を持つことができます。

### `safeTransferFrom`関数の削除
他のユーザーのNFTアセットにアクセスする時には、直接NFTアセットコントラクトにアクセスするのではなく、対象ユーザーの独自のコントラクトを通じて行う方法が推奨されます。

### ERC721との前方互換性
この提案は、既存の**ERC721**との互換性を保ちつつ、新しいアプローチを採用しています。
これにより、提案された新しい方式は、既存の**ERC721**ベースのNFTと互換性を持つことができます。

:::note info
この提案は、NFTの管理と移転をよりシンプルかつ効率的に行う方法を提供することを目指しています。
特に、スマートコントラクトウォレットを利用することで、ユーザーは自身のアセットに対してより柔軟かつ直接的な制御を行うことが可能になります。
このアプローチは、ユーザーの自律性を高めるとともに、既存の**ERC721**コントラクトの実装に伴うリスクを軽減することができます。
:::

# 仕様

この規格に準拠するコントラクトは、以下のインターフェイスを実装する必要があります。

```ts
pragma solidity ^0.8.20;

/**
 * @title ERC7561 Simple NFT interface 
 * @dev See https://ercs.ethereum.org/ERCS/erc-7561
 */
interface IERC7561 {
    /**
     * @notice Used to notify transfer NFT.
     * @param from Address of the from
     * @param to Address of the receive
     * @param tokenId The transaction token id 
     */
    event Transfer(
        address indexed from,
        address indexed to,
        uint256 indexed tokenId
    );

    /**
     * @notice  Count all NFTs assigned to an owner
     * @param owner Address of the owner
     * @return The number of NFTs owned by `owner`, possibly zero
     */
    function balanceOf(address owner) 
        external
        view
        returns (uint256);

    /**
     * @notice Find the owner of an NFT
     * @param tokenId The identifier for an NFT
     * @return The address of the owner of the NFT
     */
    function ownerOf(uint256 tokenId) 
        external  
        view
        returns (address);
	  

    /**
     * @notice Transfer ownership of an NFT
     * @param from Address of the from
     * @param to Address of the to
     * @param tokenId The NFT to transfer
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

}
```

---

### Transfer

```javascript
event Transfer(
    address indexed from,
    address indexed to,
    uint256 indexed tokenId
);
```

**概要**

NFTが`transfer`されたことを通知するイベント。

**詳細**

このイベントは、NFTの所有権があるアドレスから別のアドレスへと移転された時に発行されます。
**ERC721**や他のNFT標準で一般的に使用され、ブロックチェーン上のトランザクション記録に残ります。
イベントはインデックス付きパラメータを使用しており、これによりブロックチェーン上での検索やフィルタリングが容易になります。

**パラメータ**

- `address indexed from`
    - NFTを送信したアドレス。
- `address indexed to`
    - NFTを受信したアドレス。
- `uint256 indexed tokenId`
    - トランザクションのトークンID。

---

### `balanceOf`

```javascript
function balanceOf(address owner) 
    external
    view
    returns (uint256);
```

**概要**

特定のオーナーが所有するNFTの総数を数える関数。

**詳細**

この関数は、指定されたオーナーアドレスが保有するNFTの個数を返します。
所有しているNFTがない場合は`0`を返す可能性があります。
主に、オーナーのNFT保有量を追跡するために使用されます。

**引数**

- `address owner`
    - 所有者のアドレス。

**戻り値**

- `uint256`
    - 指定されたオーナーが保有するNFTの総数。

---

### `ownerOf`

```javascript
function ownerOf(uint256 tokenId) 
    external  
    view
    returns (address);
```

**概要**

特定のNFTのオーナーを見つける関数。

**詳細**

この関数は、指定されたトークンIDのNFTを所有しているアドレスを返します。
NFTが存在することが前提で、指定されたトークンIDに対応するオーナーのアドレスを返します。

**引数**

- `uint256 tokenId`
    - NFTの識別子。

**戻り値**

- `address`
    - NFTのオーナーのアドレス。

---

### `transferFrom`

```javascript
function transferFrom(address from, address to, uint256 tokenId) external;
```

**概要**

NFTの所有権を転送する関数。

**詳細**

この関数は、指定されたトークンIDのNFTを一つのアドレスから別のアドレスへ転送します。
この操作は、NFTの現在のオーナーまたはそれに相当する許可を得たアカウントによってのみ実行されます。

**引数**

- `address from`
    - NFTを送信するアドレス。
- `address to`
    - NFTを受信するアドレス。
- `uint256 tokenId`
    - `transfer`するNFTのトークンID。


---

# 補足

この提案は、NFT（非代替トークン）の標準をシンプル化することを目指しています。
具体的には、`approve`、`setApprovalForAll`、`getApproved`、`isApprovedForAll`、`safeTransferFrom`といった関数を削除しようとしています。
このシンプル化によって、セキュリティを強化し、複雑さを減少させ、効率を向上させることを目的としています。
その結果、スマートコントラクトウォレット環境により適した標準にしつつ、必要な機能は保持することができます。

### セキュリティの強化
削除される関数は、第三者によるNFTの管理や`transfer`を許可するものです。
これらの機能を取り除くことで、不正利用や脆弱性のリスクを減らし、セキュリティを強化できます。

### 複雑さの減少
NFTの標準からこれらの関数を削除することで、開発者が理解しやすく、実装しやすいシンプルなフレームワークを提供します。
これにより、エラーの可能性が減少し、より堅牢なシステムが構築できます。

### 効率の向上
不必要な機能を排除することで、トランザクションの処理速度が向上し、ガスコストが削減されます。
これは、特に複数のNFTを扱う場合や高いトランザクション頻度を持つアプリケーションにおいて、大きな利点となります。

## スマートコントラクトウォレット環境への適合性

スマートコントラクトウォレットは、ユーザーに高度な管理機能やセキュリティオプションを提供します。
この提案によってシンプル化されたNFT標準は、スマートコントラクトウォレットとの統合をよりスムーズにし、ユーザーが自分のデジタルアセットをより直感的に管理できるようになります。
また、スマートコントラクトウォレットにおける複雑な操作やセキュリティポリシーの実装を容易にします。

:::note info
この提案は、NFTの世界をよりアクセスしやすく、安全で、効率的なものにすることを目指しています。
シンプルながらも必要な機能を保持し、スマートコントラクトウォレットとの相互運用性を向上させることで、NFTの新しい可能性を開きます。
:::

# 互換性

### 前方互換性（Forward Compatibility）

前方互換性とは、古いシステムや標準が、新しいバージョンや標準で作成されたデータやアプリケーションと互換性を持つことを指します。
この文脈で言うと、**ERC721**がこの新しいERCと互換性を持つということは、**ERC721**に基づいて作成された既存のデータやアプリケーション（例えばNFTやDApps）が、新しいERCの環境でも機能し続けることを意味します。

### 後方互換性（Backward Compatibility）

後方互換性は、新しいシステムや標準が、古いバージョンや標準で作成されたデータやアプリケーションと互換性を持つことを指します。
つまり、この新しいERCが**ERC721**と後方互換性を持つとは、この新しいERCに基づいて作成されたデータやアプリケーションが、既存の**ERC721**環境でも問題なく動作することを意味します。

**ERC721**はこの新しいERCとの前方互換性を持ち、新しいERCは**ERC721**との後方互換性を持つことがわかります。
これは、新旧の標準がお互いに互換性を持つことを示しており、開発者が新しい標準に移行する過程で、既存のデータやアプリケーションが使え続けることを保証します。
これにより、ブロックチェーン技術の進化とともに、既存のエコシステムを維持しながら、新しい機能や改善を採用することが可能になります。

# 実装

```ts
pragma solidity ^0.8.20;

import "./IERC7561.sol";
import "../../math/SafeMath.sol";

/**
 * @title Standard ERC7561 NFT
 * @dev Note: the ERC-165 identifier for this interface is 0xc1b31357
 * @dev Implementation of the basic standard NFT.
 */
contract ERC7561 is IERC7561 {

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    mapping(uint256 tokenId => address) private _owners;

    mapping(address owner => uint256) private _balances;

    uint256 private _totalSupply;

    function totalSupply() external view returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address owner) public view  returns (uint256) {
        require (owner != address(0));
        
        return _balances[owner];
    }

    function ownerOf(uint256 tokenId) public view  returns (address) {
        return _requireOwned(tokenId);
    }


    function transferFrom(address from, address to, uint256 tokenId) public  {

        require(from == msg.sender);

        require (to != address(0) );

        address previousOwner = _update(to, tokenId);

        require(previousOwner == from);
    }


    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    function _requireOwned(uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0));
            
        return owner;
    }

    function _update(address to, uint256 tokenId) internal virtual returns (address) {
        address from = _ownerOf(tokenId);

        
        // Execute the update
        if (from != address(0)) {         

            unchecked {
                _balances[from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                _balances[to] += 1;
            }
        }

        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

}
```

# セキュリティ

このERC（Ethereum Request for Comments）提案は、既存の**ERC721**標準と後方互換性がないという点に注意が必要です。
つまり、この新しい提案は、**ERC721**に基づいて構築された現在の分散型アプリケーション（dapps）とは互換性がありません。

### 後方互換性とは？

**後方互換性**とは、新しいシステムや技術が古いバージョンや標準と互換性を持つことを意味します。
これにより、新しいシステムを導入しても、既存のアプリケーションやデータをそのまま利用できるようになります。

### この提案の影響

この提案によって生まれる新しいNFT標準は、**ERC721**をベースに開発された既存のdappsとは直接的には互換性がありません。
これは、新しい提案が**ERC721**に含まれる特定の関数を削除することでシンプル化を図っているため、これらの関数を使用している既存のdappsとの間で問題が生じる可能性があります。
例えば、新しい標準では`approve`や`transferFrom`のような関数が異なる形であるか、または全く存在しないため、これらの関数に依存する既存のdappsは、新しいNFT標準をサポートするために更新が必要になる場合があります。

### 開発者への影響

既存の**ERC721**ベースのdappsを持つ開発者は、新しい標準に移行するために、アプリケーションのアップデートや再設計が必要になるかもしれません。
これには、コードの変更、新しい機能の統合、または互換性の問題を解決するための追加の開発作業が含まれる可能性があります。
新しい標準を採用することで得られるメリット（セキュリティの強化、複雑さの減少、効率の向上）を考慮しつつ、既存のdappsとの互換性の問題をどのように解決するかが、開発者にとって重要な検討事項となります。

# 引用

Xiang (@wenzhenxiang), Ben77 (@ben2077), Mingshi S. (@newnewsms), "ERC-7561: Simple NFT, Simplified ERC-721 [DRAFT]," Ethereum Improvement Proposals, no. 7561, October 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7561.

# 最後に

今回は「**ERC721**から`safeTransferFrom`、`approve`、`setApprovalForAll`、`getApproved`、`isApprovedForAll`関数を削除し、コントラクトウォレット用に設計された**ERC7561**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下の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