# [ERC7634] NFTのTransfer回数を制限する仕組みを理解しよう！

> この規格では、ERC721を拡張してNFTのtransferの回数を制限するTransferCountの導入を提案しています。

- URL: https://cardene.dev/articles/14a75ed0f80e114e3c5e
- 公開日: 2024-06-20
- 更新日: 2024-06-20
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC721**形式のNFTの`transfer`回数を制限する仕組みを提案している**ERC7634**についてまとめていきます！

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

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

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

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

## 概要

この規格では、**ERC721**を拡張してNFTの`transfer`の回数を制限する`TransferCount`の導入を提案しています。

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

https://chaldene.net/erc721

`trasnfer`回数の設定とその情報の取得、`trasnfer`回数の追跡、`transfer`前後のステートの定義などの機能を含むインターフェースを提供します。
これにより、NFTの所有権と譲渡権をより細かく制御でき、 NFTの譲渡制限を設定できるようになります。

## 動機

NFTは一度`mint`されるとNFT作成者の手元から離れてしまいますが、プロジェクトによってはNFTの制御を行いたい場合があります。

:::message
例えば、「**アプリケーションの中でNFTを使用している場合は`transfer`できないようにする**」や「**NFTを購入できる回数を制限して希少価値を高める**」などが挙げられます。
:::

この時の利点について以下のように説明されています。

### 希少価値の保護

例えば、NFTのオークションでNFTの入札回数を制限することで、高額NFTの価格を維持できます（特にダッチオークションで効果的）。

:::message
ダッチオークションは、価格が徐々に下がっていくオークション形式です。

#### ダッチオークションの仕組み

- **価格の設定**
    - オークション開始時に、売り手が高めの初期価格を設定します。
- **価格の下落**
    - オークションが進行するにつれて、時間の経過とともに価格が徐々に下がっていきます。
- **購入タイミング**
    - 参加者は、価格が十分に下がったと感じた時点で、購入の意思を示します。
- **即決方式**
    - 誰かが購入を決定した時点で、オークションは終了します。
    - 購入者はその時点の価格で商品を購入します。

#### ダッチオークションの利点

- **迅速な販売**
    - 価格が徐々に下がるため、売れ残る可能性が低く短期間で商品を販売することができます。
- **市場価格の発見**
    - 市場参加者が最適な価格を見極めて購入するため、商品に対する実際の市場価格を効率的に発見することができます。
- **買い手の決断力**
    - 価格が下がる途中で他の参加者に先を越されるリスクがあるため、買い手は素早く決断する必要があります。これにより、購入の意思が明確な買い手が現れやすくなります。

#### 例
例えば、あるアート作品をダッチオークションで販売する場合、開始価格を100万円に設定します。
オークションが始まると、1分ごとに価格が5万円ずつ下がっていきます。
最終的に、価格が70万円になった時点で、参加者の一人がその価格で購入を決定すれば、その時点でオークションは終了し、その参加者が70万円でアート作品を手に入れます。
:::

また、特許などの知的財産では、一定の取引回数後に誰でも自由にアクセスできるように（CC0）制限して価値をコントロールできます。
ゲームにおいては、武器や装備、車両などのアイテムに取引や使用回数の制限を設けることで、使用されることで価値が減少して最終的に`burn`されるようにできます。

### セキュリティと安定性

`transfer`に制限を設けることで、高頻度取引（HFT）によるNFTアービトラージのリスクを軽減し、ネットワークのセキュリティと安定性を向上させて悪意ある取引を防止できます。

:::message
高頻度取引（HFT：High-Frequency Trading）は、アルゴリズムを用いて高速かつ大量の取引を行う手法です。

#### 特徴

- **高速取引**
    - 高性能なコンピュータと高度なアルゴリズムを使用して、極めて短い時間（ミリ秒単位やナノ秒単位）で取引を行います。
- **大量取引**
    - 一日に数百万から数億回の取引を実行することができます。
- **アルゴリズム依存**
    - 取引の意思決定はすべてアルゴリズムによって行われ、人間の介入はほとんどありません。
- **低遅延**
    - 取引の実行速度を最大化するために、取引所の近くにサーバーを配置するなどの手法が用いられます。

#### 高頻度取引の利点

- **市場の流動性向上**
    - 高頻度取引は大量の注文を発生させるため、市場の流動性を向上させ、取引が活発になります。
- **価格発見の効率化**
    - 高頻度取引のアルゴリズムは市場の微細な価格差を迅速に見つけて取引するため、価格の発見が効率的に行われます。
- **取引コストの低減**
    - 高頻度取引は極めて短い時間内に取引を完了させるため、スプレッド（買値と売値の差）が狭くなり、取引コストが低減します。

#### リスク

- **市場のボラティリティ増大**
    - 短期間に大量の注文が行われるため、市場の価格変動が激しくなることがあります。
- **フラッシュクラッシュ**
    - アルゴリズムの誤動作や大量の注文キャンセルが同時に発生すると、一瞬で市場価格が急落するフラッシュクラッシュを引き起こすリスクがあります。
- **公平性の問題**
    - 高頻度取引業者は一般投資家に比べて取引スピードで大きな優位性を持っており、市場の公平性が疑問視されることがあります。
- **規制の課題**
    - 高頻度取引の規制は国や地域によって異なり、適切な規制が整っていない市場ではリスクが高まる可能性があります。
:::

### 経済的リスクの軽減

NFTのリステーキングにおける取引回数を制限することで、経済的なリスクを軽減してバブルを防ぎます。
例えば、EigenLayer（Ethereum）やBabylon（Bitcoin）、Picasso（Solana）のようなプラットフォームでは、NFTがリステークポジション（再ステークの証明）として使用されています。
これが繰り返し市場にリステークされると、レバレッジが増加し、バブルが生じる可能性があります。
取引回数に制限を設けることで、ステーキングエコシステム内でのポンジスキームのような動きを予防できます。

### 重要なポイント

NFTに関する新しい標準（スタンダード）が提供するいくつかの利点について説明します。

#### 主なポイント

##### 価値の保存のコントロール
NFTの作成者がカスタマイズされた`transfer`制限を設定することでデジタル資産の価値を維持することができます。
物理的なコレクターズアイテムが希少性によって価値を持つように、NFTの転送回数を制限することで、その価値が長期間保たれることを保証します。

##### 意図された使用方法の確保
`transfer`を制限を設定することで、NFTが本来の目的に沿って使用されるようになります。
例えば、限定版のデジタルアートを表すNFTであれば、`transfer`を制限することで、過度な取引抑制して価値の低下を防ぐことが可能になります。

##### 使用ケースの拡大
これらの機能強化により、NFTのユースケースが広がります。
例えば、会員権やライセンスを表すNFTに限定的な`transfer`機能を持たせることで、デジタル所有権の新しいモデルを実装できます。

##### 簡単な統合
この標準は既存の**ERC721**インターフェースを拡張して、新しい機能を含む別のインターフェース（**IERC7634**）を定義しています。
互換性を維持しつつ、現在のNFTプロジェクトに`transfer`制限をシームレスに組み込むことができます。

## 仕様

この規格に準拠するコントラクトは以下を実装する必要があります。

```js
pragma solidity ^0.8.4;

/// @title IERC7634 Interface for Limited Transferable NFT
/// @dev Interface for ERC7634 Limited Transferable NFT extension for ERC721
/// @author Saber Yu

interface IERC7634 {

    /**
     * @dev Emitted when transfer count is set or updated
     */
    event TransferCount(uint256 indexed tokenId, address owner, uint256 counts);

    /**
     * @dev Returns the current transfer count for a tokenId
     */
    function transferCountOf(uint256 tokenId) external view returns (uint256);

    /**
     * @dev Sets the transfer limit for a tokenId. Can only be called by the token owner or an approved address.
     * @param tokenId The ID of the token for which to set the limit
     * @param limit The maximum number of transfers allowed for the token
     */
    function setTransferLimit(uint256 tokenId, uint256 limit) external;

    /**
     * @dev Returns the transfer limit for a tokenId
     */
    function transferLimitOf(uint256 tokenId) external view returns (uint256);
}
```

- `setTransferLimit`
    - 指定した`tokenId`のNFTの`transfer`制限を設定する関数。
- `transferLimitOf`
    - 指定した`tokenId`のNFTの`transfer`制限を取得する関数。
- `transferCountOf`
    - 指定した`tokenId`のNFTの現在の`transfer`回数を返す関数。

## 補足

#### `transfer`回数の追跡が重要かどうか

`transfer`回数の追跡は任意であり、実装は要件に左右されます。

実装する場合は、`transfer`回数をカウントアップする`_incrementTransferCount`関数と`transfer`できる上限値を取得する`transferLimitOf`関数、現在の`transfer`回数を取得する`transferCountOf`関数を実装する必要があります。

#### 他の追跡したいデータ

`transfer`前後で実行される`_beforeTokenTransfer`と`_afterTokenTransfer`関数をオーバーライドして、NFTの`transfer`前後の状態を定義できます。
これらの関数は、`transfer`制限や回数に沿って必要なチェックや更新を行うために使用されます。

## 互換性

この規格は**ERC721**と互換性を持ちます。

### 拡張機能

#### `burn`機能

**ERC5679**の`burn`機能を統合することで、NFTが一定の`transfer`制限に達した後に自動的に`burn`することができます。
この機能は、Snapchatのメッセージが複数回閲覧された後に消える仕組みに似ています。

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

[\[EIP5679\] ERC20, ERC721, ERC1155のMintとBurnの実装を統一しよう！](/articles/3be55bed3c7793830513)

### SBTとの統合

`transfer`ができない仕組みを提供しているSBT（Soulbound Tokens）と統合することで、NFTが一定の取引回数に達した後に`transfer`できなくなる仕組みを実装できます。
この機能は、複数回の入札ラウンドを経て最終的に入札者がアイテムを手に入れるシナリオに似ています。

## 参考実装

```js

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./IERC7634.sol";

/// @title Limited Transferable NFT Extension for ERC721
/// @dev Implementation of the Limited Transferable NFT extension for ERC721
/// @author Saber Yu

contract ERC7634 is ERC721, IERC7634 {

    // Mapping from tokenId to the transfer count
    mapping(uint256 => uint256) private _transferCounts;

    // Mapping from tokenId to its maximum transfer limit
    mapping(uint256 => uint256) private _transferLimits;

    /**
     * @dev See {IERC7634-transferCountOf}.
     */
    function transferCountOf(uint256 tokenId) public view override returns (uint256) {
        require(_exists(tokenId), "ERC7634: Nonexistent token");
        return _transferCounts[tokenId];
    }

    /**
     * @dev See {IERC7634-setTransferLimit}.
     */
    function setTransferLimit(uint256 tokenId, uint256 limit) public override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC7634: caller is not owner nor approved");
        _transferLimits[tokenId] = limit;
    }

    /**
     * @dev See {IERC7634-transferLimitOf}.
     */
    function transferLimitOf(uint256 tokenId) public view override returns (uint256) {
        require(_exists(tokenId), "ERC7634: Nonexistent token");
        return _transferLimits[tokenId];
    }

    /**
     * @dev Internal function to increment transfer count.
     */
    function _incrementTransferCount(uint256 tokenId) internal {
        _transferCounts[tokenId] += 1;
        emit TransferCount(tokenId, ownerOf(tokenId), _transferCounts[tokenId]);
    }

    /**
     * @dev Override {_beforeTokenTransfer} to enforce transfer limit.
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override {
        require(_transferCounts[tokenId] < _transferLimits[tokenId], "ERC7634: Transfer limit reached");
        super._beforeTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Override {_afterTokenTransfer} to handle post-transfer logic.
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 quantity
    ) internal virtual override {
        _incrementTransferCount(tokenId);

        if (_transferCounts[tokenId] == _transferLimits[tokenId]) {
            // Optional post-transfer operations once the limit is reached
            // Uncomment the following based on the desired behavior such as the `burn` opearation
            // ---------------------------------------
            // _burn(tokenId); // Burn the token
            // ---------------------------------------
        }

        super._afterTokenTransfer(from, to, tokenId, quantity);
    }


    /**
     * @dev Override {supportsInterface} to declare support for IERC7634.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC7634).interfaceId || super.supportsInterface(interfaceId);
    }
}
```


各NFT作成者が`transfer`制限を設定できる必要があります。
また、改竄や不正を防ぐために、一度設定した`transfer`制限は変更できないようすることを検討する必要があります。
実行中にガス制限を超える可能性があるような複雑な処理を実行するシステムと統合する場合は、リソースを大量に消費する操作を実行しないように注意が必要です。


## 引用

Qin Wang (@qinwang-git), Saber Yu (@OniReimu), Shiping Chen <shiping.chen@data61.csiro.au>, "ERC-7634: Limited Transfer Count NFT [DRAFT]," Ethereum Improvement Proposals, no. 7634, February 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7634.

## 最後に

今回は「**ERC721**形式のNFTの`transfer`回数を制限する仕組みを提案している**ERC7634**」についてまとめてきました！
いかがだったでしょうか？

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