# [ERC5173] 過去のNFT保有者もロイヤリティを取得できる仕組みを理解しよう！

> 提案されているERC7507は、ERC721トークン（NFT）の所有者に、トークンを売却した後でも将来の価格上昇から利益を得る機会を提供するものです。

- URL: https://cardene.dev/articles/2133591db882b3e096e5
- 公開日: 2023-10-25
- 更新日: 2023-10-25
- トピック: Blockchain
- タグ: ethereum, solidity, ブロックチェーン, nft

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、非代替性トークン（NFT）の過去の所有者全員に報酬を与える多世代報酬メカニズムを提案している規格である**ERC5173**についてまとめていきます！

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

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

:::message alert
**5173**は現在（2023年10月21日）では「**Draft**」段階です。
:::

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

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

## 概要

提案されている**ERC7507**は、**ERC721**トークン（NFT）の所有者に、トークンを売却した後でも将来の価格上昇から利益を得る機会を提供するものです。

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

https://chaldene.net/erc721

これは「**未来のリワード（FR）拡張**」と呼ばれています。
また、**ERC5173**を用いて、クリエイター、バイヤー、セラーが協力して価値を増加させ、共に富を築く「**参加型バリューアンプリフィケーションフレームワーク**」を導入します。
この斬新なアプローチは、トレーダーが直面する課題に対処し、ゼロサムの考え方を超えた、公平でより利益の多いシステムを構築します。
サービス提供者とユーザーの利益が一致することで、持続可能で有益な取引環境が生まれます。
**ERC5173**に適合するトークン所有者は、所有中に価格の上昇を享受できるだけでなく、トークンを売却した後も未来のリワード（FR）を継続的に受け取ることができます。
分割を排除し、共同繁栄を奨励することで、**ERC5173**は参加者間の強い結びつきを育みます。
オリジナルの`Minter`を含むすべてのセラーは、NFT未来のリワード（**nFR**）フレームワークを通じて均等なFR分配を受け取り、これまで所有していたユーザーと利益を公平に共有できます。

## 動機

現在の取引環境は、スプーフィング、インサイダートレーディング、ウォッシュトレーディングなどの不正な実践が広がっており、これらの手法は一般のトレーダーに損失をもたらすことがよくあります。

:::message
**スプーフィング**
市場での注文や取引を偽装する行為です。
具体例として、トレーダーAが特定の株式を大量に売りたいと考えているとしましょう。
しかし、市場価格が下がらないように見せかけたい場合、彼らは大量の買い注文を出し、市場に影響を与えます。
他のトレーダーは、価格が上昇すると思い込み、買い注文を出す可能性が高くなります。
トレーダーAはその後、実際に売りたい数量を売却し、価格が下がることを期待しています。
これにより、他のトレーダーは誤った情報に基づいて取引を行い、トレーダーAは不正な利益を得ることができます。

**インサイダートレーディング**
非公開の情報を利用して市場での取引を行う行為です。
具体例として、ある企業の役員が新製品の発表を数週間前に知っているとしましょう。
この情報はまだ一般の投資家には公開されていないため、役員はその情報を利用して、企業株式を購入するか売却するかを決定します。
このように非公開の情報を持つ者は市場で不正な利益を得ることができますが、これは合法ではありません。

**ウォッシュトレーディング**
トレーダーが自分自身と取引を行う行為で、主要な目的は実際の取引よりも価格を操作し、他の投資家を欺くことです。
具体例として、トレーダーBが株式Xを持っており、価格を上げるために株式Xを買い続けます。
同時に、トレーダーBは別の口座で株式Xを売り続けます。
この取引は実際の売買ではなく、価格を操作するための操作です。
他の投資家は価格の上昇に騙され、株式Xを購入するかもしれませんが、実際には価格は操作されており、トレーダーBにとって利益をもたらすことが目的です。

これらの不正な取引プラクティスは市場の公平性と透明性に対する脅威となり、規制当局によって厳しく監視されています。
:::

これらのトレーダーは欲望と恐れのサイクルに巻き込まれてしまいます。
しかし、非代替可能トークン（NFT）の登場と、すべての取引を追跡できる可能性があることから、この不平等な価値分配を変える機会があります。

**ERC5173**の実装は、トークンの所有履歴全体にわたる利益の共有の基準を設け、市場参加者全体に利益をもたらします。
これにより、バイヤーや所有者が価格発見プロセスへの貢献に報酬を受ける「**価値増幅システム**」が導入されます。
このモデルは利益を一致させ、バイヤーとセラーの両方にとって相互に有益な経済的ルールを作り出します。

NFTは、物理的なアートやコレクションとは異なり、所有者の価値への貢献を正確に反映できます。
各**ERC721**トークンの価格変動を記録することで、「**未来のリワードプログラム**」を確立し、所有者に公平な報酬を提供します。
このプログラムはフィールドを均等にし、一般のトレーダーに成功の機会を提供することを目指しています。

さらに、新しいギフト経済モデルを推進するだけでなく、この規格で提案しているフレームワークはアーティストやマーケットプレイスが設定したルールを回避する不正な取引を防ぎます。
取引エコシステム内で透明性と誠実さを促進します。

また、**ERC20**トークンのラップトークン取引にこの価値増幅フレームワークを適用すると、アイデンティティが取引の時価とセールスデータに組み込まれ、資産取引業界が根本的に変わります。
この包括的な機能により、各取引に新たな視点が加わり、取引が新たな次元に進化します。

:::message
**ERC20**については以下を参考にしてください。
:::

https://chaldene.net/erc20

## 仕様

**ERC721**準拠のコントラクトは、未来の買い手と以前の所有者に将来の利益を報酬として提供するための標準的な方法を提供するために、このEIPを導入できます。

このEIPを実装する際、コントラクトは次の2つの重要な関数を持つ必要があります。

- `awardFutureBuyer`
    - 未来の購入者に報酬を与えるための関数。
- `awardPreviousOwners`
    - 以前の所有者に報酬を与えるための関数。

このEIPにより、**ERC721**トークンの取引において、将来の利益を所有者間で共有する機会が提供されます。
これにより、トークンの所有者や取引参加者が未来の利益を公平に分け合う手段が提供され、トークンエコシステム全体の持続可能性が向上します。

```javascript
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/*
 *
 * @dev Interface for the Future Rewards Token Standard.
 *
 * A standardized way to receive future rewards for non-fungible tokens (NFTs.)
 *
 */
interface IERC5173 is IERC165 {

    event FRClaimed(address indexed account, uint256 indexed amount);

    event FRDistributed(uint256 indexed tokenId, uint256 indexed soldPrice, uint256 indexed allocatedFR);

    event Listed(uint256 indexed tokenId, uint256 indexed salePrice);

    event Unlisted(uint256 indexed tokenId);

    event Bought(uint256 indexed tokenId, uint256 indexed salePrice);

    function list(uint256 tokenId, uint256 salePrice) external;

    function unlist(uint256 tokenId) external;

    function buy(uint256 tokenId) payable external;

    function releaseFR(address payable account) external;

    function retrieveFRInfo(uint256 tokenId) external returns(uint8, uint256, uint256, uint256, uint256, address[] memory);

    function retrieveAllottedFR(address account) external returns(uint256);

    function retrieveListInfo(uint256 tokenId) external returns(uint256, address, bool);
    
}
```

#### `list`

```javascript
function list(uint256 tokenId, uint256 salePrice) external;
```

**概要**

NFTトークンをリストに追加し、販売価格を設定する関数。

**詳細**

この関数を使用すると、指定されたトークンIDのNFTがリストに追加され、販売価格が設定されます。
これにより、他のユーザーがNFTを購入する時に、販売価格が表示されます。

**引数**

- `tokenId`
  - トークンの一意の識別子。
- `salePrice`
  - NFTの販売価格。

---

#### `unlist`

```javascript
function unlist(uint256 tokenId) external;
```

**概要**

指定されたトークンIDのNFTをリストから削除する関数。

**詳細**

この関数を使用すると、指定されたトークンIDのNFTがリストから削除されます。
これにより、NFTの販売が取り消されます。

**引数**

- `tokenId`
  - トークンの一意の識別子。

---

#### `buy`

```javascript
function buy(uint256 tokenId) payable external;
```

**概要**

指定されたトークンIDのNFTを購入する関数。

**詳細**

この関数を使用すると、指定されたトークンIDのNFTを購入することができます。
購入者はETHを支払い、NFTを所有することができます。

**引数**

- `tokenId`
  - トークンの一意の識別子。

---

#### `releaseFR`

```javascript
function releaseFR(address payable account) external;
```

**概要**

指定されたアカウントに未来の報酬（**FR**）をリリースする関数。

**詳細**

この関数を使用すると、指定されたアカウントに未来の報酬（**FR**）がリリースされます。
これにより、アカウント所有者は**FR**を受け取ることができます。

**引数**

- `account`
  - **FR**を受け取るアカウントのアドレス。

---

#### `retrieveFRInfo`

```javascript
function retrieveFRInfo(uint256 tokenId) external returns(uint8, uint256, uint256, uint256, uint256, address[] memory);
```

**概要**

指定されたトークンIDに関する未来の報酬（**FR**）の情報を取得する関数。

**詳細**

この関数を使用すると、指定されたトークンIDに関連する未来の報酬（**FR**）の情報が取得されます。
これにはFRの種類、合計額、分配可能な額、現在の所有者などが含まれます。

**引数**

- `tokenId`
  - トークンの一意の識別子。

**戻り値**

- `uint8`
  - FRの種類。
- `uint256`
  - FRの合計額。
- `uint256`
  - 分配可能なFRの額。
- `uint256`
  - 現在の所有者の未来の報酬（FR）の額。
- `uint256`
  - 前の所有者の未来の報酬（FR）の額。
- `address[] memory`
  - NFTの所有者リスト。

---

#### `retrieveAllottedFR`

```javascript
function retrieveAllottedFR(address account) external returns(uint256);
```

**概要**

指定されたアカウントに割り当てられた未来の報酬（**FR**）の合計額を取得する関数。

**詳細**

この関数を使用すると、指定されたアカウントに割り当てられた未来の報酬（**FR**）の合計額が取得されます。

**引数**

- `account`
  - FRの割り当てを取得するアカウントのアドレス。

**戻り値**

- `uint256`
  - アカウントに割り当てられた未来の報酬（**FR**）の合計額。

---

#### `retrieveListInfo`

```javascript
function retrieveListInfo(uint256 tokenId) external returns(uint256, address, bool);
```

**概要**

指定されたトークンIDに関するリスト情報を取得する関数。

**詳細**

この関数を使用すると、指定されたトークンIDに関連するリスト情報が取得されます。
これにはトークンの価格、販売者のアドレス、リスト状態（有効または無効）などが含まれます。

**引数**

- `tokenId`
  - トークンの一意の識別子。

**戻り値**

- `uint256`
  - トークンの価格。
- `address`
  - 販売者のアドレス。
- `bool`
  - リストの状態（有効または無効）。

---

**nFR（NFT Future Rewards）**コントラクトは、各トークンIDごとに実装および更新する必要があります。
`FRInfo`構造体内のデータは、1つのマッピングにすべて格納するか、複数のマッピングに分割することができます。
この構造体は、データにアクセスするための方法として、公開のマッピングまたは複数のマッピングを提供するか、あるいは非公開のデータにアクセスするための公開関数を持たなければなりません。
これはクライアントがデータを取得し、検証するためのものです。

:::message
**nFR**コントラクトは、各トークンIDに関連するデータを適切に格納し、クライアントがそのデータを取得し、検証するための方法を提供しなければなりません。
これにより、トークンの未来のリワードに関連する情報にアクセスでき、データの整合性が保証されます。
:::

```javascript
struct FRInfo {
        uint8 numGenerations; //  Number of generations corresponding to that Token ID
        uint256 percentOfProfit; // Percent of profit allocated for FR, scaled by 1e18
        uint256 successiveRatio; // The common ratio of successive in the geometric sequence, used for distribution calculation
        uint256 lastSoldPrice; // Last sale price in ETH mantissa
        uint256 ownerAmount; // Amount of owners the Token ID has seen
        address[] addressesInFR; // The addresses currently in the FR cycle
}

struct ListInfo {
        uint256 salePrice; // ETH mantissa of the listed selling price
        address lister; // Owner/Lister of the Token
        bool isListed; // Boolean indicating whether the Token is listed or not
}
```

さらに、**nFR（NFT Future Rewards）**スマートコントラクトは、各トークンIDに対応する`ListInfo`をマッピングに格納する必要があります。
また、トークンIDの対応する`ListInfo`を取得するための方法は、公にアクセス可能である必要があります。
これは、トークンごとに関連する情報を保持し、必要な場合に取得できるようにするためです。

また、**nFR**スマートコントラクトは、特定のアドレスに割り当てられたEtherの量を`_allotedFR`マッピングに格納し、更新する必要があります。
`_allotedFR`マッピングは、公開されているか、または特定のアドレスに割り当てられた**FR**支払いを取得するための公開関数を持っている必要があります。
これにより、アドレスごとに割り当てられた**FR**支払い情報を追跡および確認できます。

### 割合の固定小数点
`_allocatedFR`は、割合の固定小数点を使用して計算され、スケーリングファクターは`1e18（X/1e18）`です。
たとえば、`5%`の場合は`5e16`などです。
これは、標準を均一に保つために必要であり、割合を正確に表現します。最大値と最小値は`-1e18 - 1`です。

### デフォルトのFR情報
また、**ERC721**の`mint`関数との後方互換性を維持するために、デフォルトの`FRInfo`が格納される必要があります。
さらに、デフォルトの`FRInfo`を更新するための関数が提供されることがあります。
このデフォルトの情報は、トークンの新規作成時に使用され、必要に応じて変更できます。

### ERC721 オーバーライド
**nFR（NFT Future Rewards）**コントラクトは、**ERC721**の特定の関数をオーバーライドする必要があります。
これにより、トークンの取引における未来の価値共有が実現できます。
具体的には、以下の3つの関数をオーバーライドする必要があります。

#### `_mint`
新しいトークンを発行する時に呼び出されます。
**nFR**コントラクトでは、デフォルトの未来のリワード（**FR**）モデルを確立し、新しいトークンの所有者に対して**FR**サイクルに参加する権利を与えるためにこの関数をオーバーライドする必要があります。

#### `_transfer`
トークンの所有権を転送する時に呼び出されます。
**nFR**コントラクトでは、トークンが`0`ETHで売却されたとみなし、`transfer`が成功した後に状態を更新する必要があります。
また、**FR**回避を防ぐために、呼び出し元がトークンを自身に転送したり、**FR**スライディングウィンドウ内のアドレスに転送したりすることを防ぐ必要があります。

#### `_burn`
トークンを焼却（削除）する時に呼び出されます。
**nFR**コントラクトでは、成功したバーン後にそのトークンIDに関連する**FR**情報と`ListInfo`を削除する必要があります。

さらに、安全な`transfer`や`mint`を実現するために、ミントとトランスファーの後に**ERC721**の`_checkOnERC721Received`関数を明示的に呼び出すことも考慮されます。
これにより、トークンの安全性が確保されます。

### 安全な`transfer`
ウォレット、ブローカー、オークションアプリケーションが安全な`transfer`を受け入れるためには、**ERC721**ウォレットインターフェースを実装する必要があります。
これは、トークンの安全な移動や取引を確保するための標準的な方法です。

### リスティング、アンリスティング、および購入
`list`、`unlist`、`buy`（購入）関数は、トークンを販売するための重要な機能です。
これらの関数を実装することにより、トークンの売買プロセスが可能になります。
`list`関数はトークンを売り出すためにリスティングし、`unlist`関数はトークンの売り出しを取り消し、`buy`関数は他のユーザーからトークンを購入するために使用されます。
これらの機能により、トークンの市場での取引がスムーズに行われます。

```javascript
function list(uint256 tokenId, uint256 salePrice) public virtual override {
   //...
}


function unlist(uint256 tokenId) public virtual override {
   //...
}

function buy(uint256 tokenId) public virtual override payable {
   //...
}
```

#### `list`
トークンのID（`tokenId`）と販売価格（`salePrice`）を受け取り、トークンの所有者または承認済みのユーザー（`msg.sender`）であることを確認した後、指定された`tokenId`に対応する`ListInfo`を更新する関数。
この関数は、トークンが売り出されており、どの価格で売り出されているかを示します。
リスト関数の実行後`Listed`イベントを発行します。

#### `unlist`
トークンのID（`tokenId`）を受け取り、所有者の確認が行われた後、対応する`ListInfo`を削除する関数。
この関数は、トークンのリストから削除されたことを示すために`Unlisted`イベントを発行します。

#### `buy`
トークンのID（`tokenId`）を受け取り、支払いが可能である必要があります。
トークンの販売価格（`salePrice`）と`msg.value`（トランザクションで送信されたEtherの量）が一致し、かつトークンがリストされているかどうかを確認します。
条件を満たす場合、関数は**FR**コントラクトの`_transferFrom`関数を呼び出し、トークンの取引を完了します。
さらに、この関数は購入者がすでに**FR**スライディングウィンドウ内に存在しないことを確認します。
これにより、トークンの取引が有効であることが確認され、必要な**FR**がコントラクト内で保持されます。
この関数の実行後`Bought`イベントを発行します。

これらの関数を使用することで、トークンのリストと取引が管理され、安全に実行されることが保証されます。

### 将来の報酬: `_transferFrom`関数

**nFR（NFT Future Rewards）**サポートを提供するすべてのスマートコントラクトから呼び出される必要がありますが、非**nFR**サポートのコントラクトとの後方互換性も考慮できます。

この関数は、以下のように定義されています。

```solidity
function transferFrom(address from, address to, uint256 tokenId, uint256 soldPrice) public virtual override payable {
    //...
}
```

この関数は、トークンの最後に売却された価格（`lastSoldPrice`）をもとに、トランザクションが利益があるかどうかを判断します。
**ERC721**の`transfer`関数を呼び出してNFTを転送した後、トランザクションが利益がない場合、スマートコントラクトは以下の処理を実行します。

1. 対応するトークンIDの`lastSoldPrice`を更新します。
2. 所有者の金額を増加させます。
3. 所有権情報を更新します。
4. `msg.value`（トランザクションに添付されたEtherの量）をリストアップした者に転送します。

一方、トランザクションが利益がある場合、スマートコントラクトは以下の処理を実行します。

1. `_distributeFR`関数を呼び出します。
2. `lastSoldPrice`を更新します。
3. 所有者の金額を増加させます。
4. 所有権情報を更新します。

`_distributeFR`関数または**FR**コントラクトの`_transferFrom`関数は、`_addressesInFR`間で分配される割り当てられた**FR**と、リストアップした者への`msg.value`の差を返す必要があります。
操作が完了したら、関数は対応する`ListInfo`をクリアします。

この関数のオーバーライドでは、受信者がトークンの送信者またはFRスライディングウィンドウ内のアドレスでないことを確認する必要があります。
これにより、トランザクションの妥当性が確保されます。

### 将来の報酬計算

未来の報酬の計算について詳しく説明します。

この規格をサポートするコントラクトでは、NFTトークンの取引に関連する利益を前の所有者と共有するための方法を提供します。

```solidity
function _calculateFR(uint256 totalProfit, uint256 buyerReward, uint256 successiveRatio, uint256 ownerAmount, uint256 windowSize) pure internal virtual returns(uint256[] memory) {
    //...        
}
```

具体的な例を考えてみましょう。
売り手は、自身のトークンを売却した時に、その取引から得られた純利益の一部を、そのトークンの前の10人の所有者と共有する必要があります。
また、トークンが取引を重ねるにつれて、その価値が上昇する場合、同じ売り手にも未来の報酬が支払われます。

しかし、所有者が保有期間中に損失を出した場合、彼らはその取引から得られた利益を共有する責任を負う必要はありません。
ただし、彼らは利益がある場合、将来の所有者の世代からの未来の報酬を受け取るべきです。

![Total_FR_Payout_Distribution-geo.png](/images/qiita-e6134a95-6cdf-c128-5ceb-b893b467b131.webp)
図1: **幾何学的配列分布**
引用: https://eips.ethereum.org/EIPS/eip-5173

購入者/所有者はNFTトランザクションから実際に得られた利益（P）の一部（r）を受け取ります。
残りの収益は売り手に戻ります。

スライディングウィンドウメカニズム（n）を導入することにより、以前のどの所有者が報酬を受け取るかが決まります。
所有者はキューに配置され、最も古い所有者から始まり、現在の所有者の一つ前の所有者（最後の世代）で終わります。
最初の世代は、次のn世代の最後の所有者です。
利益の分配ウィンドウは、最初の世代から最後の世代までの一定のサイズで定義されます。

利益の分配は、ウィンドウ内にある前の所有者にのみ利用可能であるべきです。

この例では、利益が幾何学的なシーケンスに基づいて分配され、最後の世代の所有者（現在の売り手の直前の所有者）には一部の利益が与えられます。
残りの利益は、**Mid-Gen**の所有者に行き、早くトークンを購入したほど多くの利益が得られます。
スライディングウィンドウで決定される最後の有資格な所有者は、最初の世代です。
従って、初期の所有者は最も多くの報酬を受け取ることになります。

### 将来の報酬分配

![nFR_Standard_Outline.jpeg](/images/qiita-c3ff656c-0b1d-fca2-557c-4ce21ed790a7.webp)
図2: **NFTオーナーの将来の報酬（nFR）**
引用: https://eips.ethereum.org/EIPS/eip-5173

図2は、所有者が実際の取引で得た利益に基づいて計算される、5つの世代にわたる未来の報酬分配プログラムの例を示しています。

```solidity
function _distributeFR(uint256 tokenId, uint256 soldPrice) internal virtual {
    //...
    emit FRDistributed(tokenId, soldPrice, allocatedFR);
}
```

この`_distributeFR`関数は、取引が利益を生じる場合**FR**コントラクトの`_transferFrom`関数内から呼び出される必要があります。
この関数は、**FR**の分配対象となるアドレスを決定しますが、`addressesInFR`内の最後のアドレスを除外して計算されます。
関数が分配対象のアドレスが存在しない場合、つまり初めての売却の場合、**FR**の支払いを処理するか、`_transferFrom`が**FR**の支払いを処理している場合、`0`を返すか、`msg.value`をリストアップした者に送信します。
関数は、現在の売却価格と`lastSoldPrice`の差を計算し、適切な**FR**の分配を受けるために`_calculateFR`関数を呼び出します。
その後、必要に応じて順序を調整しながら**FR**を分配します。
その後、コントラクトは分配された**FR**の合計額（`allocatedFR`）を計算し、その差額を`soldPrice`と`allocatedFR`の間でリストアップした者に返します。
最後に、`FRDistributed`イベントを発行します。
また、関数は`allocatedFR`を返すことができます。
これは、`_transferFrom`関数が`allocatedFR`をリストアップした者に送信する場合に使用されます。

### 将来の報酬請求

未来の報酬の受け取り
未来の報酬の支払いは、OpenZeppelinのPaymentSplitter契約で示されているような、引き出し型の支払いモデルを使用すべきです。このモデルでは、ユーザーが自分のタイミングで未来の報酬を受け取ることができます。支払いが成功した場合、FRClaimedというイベントがトリガーされるでしょう。

以下は未来の報酬を受け取るための関数です：

```solidity
function releaseFR(address payable account) public virtual override {
    //...
}
```

この関数は、引数として指定されたアカウント（`address payable account`）に対して未来の報酬を引き出すために使用されます。
ユーザーは自分のアカウントを指定して、未来の報酬を受け取ることができます。
関数が成功すると、`FRClaimed`イベントが発行され、未来の報酬の受け取りが記録されます。

この引き出し型の支払いモデルを使用することで、ユーザーは自分の希望するタイミングで未来の報酬を受け取ることができ、柔軟性が向上します。

### 所有者の交代

所有者の世代のシフトとは、トークンの所有権が移転する時に以前の所有者世代から新しい所有者世代への移行を管理するプロセスです。
このプロセスは、「`_shiftGenerations`」関数を通じて実施されます。
この関数は、トークンの売却が利益があるかどうかに関係なく、トークンの所有権が移転されるたびに呼び出されます。

具体的には、以下のステップで機能します。

1. トークンの所有権が移転されると、以前の所有者から新しい所有者へのトークンの所有権が移転されます。
2. この所有権の移転に伴い、以前の所有者の一部は所有権を喪失し、新しい所有者になります。
これにより、所有者の世代が進行します。
3. 最も古い所有者は、トークンの持ち主のリストから削除されます。
これは、トークンの管理と未来の報酬の計算において重要です。
4. 現在のトークンの所有者が新しい所有者で更新され、新しい所有者の情報がリストに追加されます。

このプロセスにより、コントラクトはトークンの過去の所有者を追跡し、未来の報酬の計算や必要な処理を効果的に行うことができます。
新しい所有者がトークンを取得するたびに、最も古い所有者が世代から外れ、新しい所有者が追加され、過去の所有者が正確に管理されます。

## 補足

### `10^18`の固定パーセント

固定小数点演算では、`1e18`は`100%`を表し、`1e16`は`1%`を表します。
この方法は、多くのSolidityライブラリで見られる固定小数点演算の一般的なアプローチです。

支払いのためのイベントの発行に関して、各NFTコントラクトは独立して存在し、アイテムが売却された場合にはマーケットプレイスコントラクトからイベントを発行できます。
しかし、支払いのためにもイベントを発行することは重要です。
なぜなら、ロイヤルティや未来の報酬の受取人が、NFTが二次売却されたことを把握していないか、監視していないかもしれないためです。
そのため、支払いが行われたことを確認できる方法が必要です。
これは、支払いの受取人がETHウォレットの残高を確認する以外の手段を提供します。

このため、二次売却の受取人は、支払いが行われたことを確認するために、NFTの売却コントラクトの親コントラクトを呼び出すことができます。
これにより、支払いがトランザクションイベントとして記録され、関係者が支払いの詳細を正確に追跡し、確認できるようになります。
**ERC2981**も同様の方法を採用しており、支払いの透明性と確実性を向上させるための有用なパターンです。

:::message
**ERC2981**については以下を参考にしてください。
:::

[\[ERC2981\]NFTのロイヤリティ強制化!? ERC2981の仕組みについて理解しよう！](/articles/456ccacca080fd72e3ec)

### 歴代オーナー数(n) vs 黒字オーナーのみの数

歴代所有者全員の数（n）と利益を上げた所有者だけのオーナー数との比較について、後の所有者との利益の共有に影響を与えるのは、利益を上げた所有者だけでなくすべての所有者の世代数であることです。
このポイントは、図3で視覚的に示されています。

具体的に言うと、所有者全員の世代数（n）は、NFTの後継の所有者との利益共有を決定する際に重要な要素です。
つまり、NFTを保有しているすべての所有者の世代数が考慮されます。
もしNFTを保持しているすべての所有者が損失を被る場合、未来の報酬の分配は現在の所有者や購入者には行われないということです。
詳細な情報についてはセキュリティに関する考慮事項で説明されています。

このアプローチは、「**所有権の蓄積**」を防ぐために採用されており、NFTを保有しているすべての所有者が損失を被る場合、現在の所有者や購入者に未来の報酬の分配が行われないようになっています。
これにより、NFTの取引を促進し、所有者がNFTを保有し続けることを抑制する効果があります。

![Losing_owners.jpeg](/images/qiita-58b1cfef-923d-7906-594e-393437dc4873.webp)
図3: 損失を被ったオーナー
引用: https://eips.ethereum.org/EIPS/eip-5173

### 単一世代と多世代

単一の世代報酬システムでは、新しい購入者や所有者は、次の単一の世代の実現した利益の一部しか受け取りません。
つまり、利益を上げた最初の所有者からのみ報酬が支払われます。
一方、マルチジェネレーション報酬システムでは、購入者は購入後数年後にも未来の報酬を受け取る可能性があります。
これは、NFTが長期的な成長の潜在性を持つ場合に特に有益です。
この場合、複数の世代にわたって報酬が支払われ、配当金が蓄積されることがあります。

我々は、マーケットプレイスのオペレーターが単一の世代分配システムとマルチジェネレーション分配システムのいずれかを選択できるよう提案しています。
つまり、どちらの報酬システムを採用するかは、プラットフォームの運営者に委ねられます。
これにより、異なるNFTコレクションやプロジェクトに適した報酬モデルを選択できるようになります。

### 売り手の直接**FR**支払いとコントラクトによる支払い管理の比較

売却収益から直接派生する**FR**支払いは、即座に行われ最終的な支払いです。
これは、詐欺の検出やセキュリティの観点から重要です。
スマートコントラクトは、前の所有者の各世代に対するすべての**FR**金額を計算し、マーケットプレイスが設定した他の基準に従って支払いを処理します。
たとえば、ウォレットアドレスのスコアが低い場合や、時間ヒューリスティック分析を使用して検出された一連の連続注文などの条件に従って、支払いが行われます。

つまり、**FR**支払いは売却収益から直接行われるか、スマートコントラクトによって計算および管理される方法があります。
前者は即座かつ最終的な支払いを提供し、後者は柔軟性を持ち、さまざまな条件に基づいて支払いを処理できる利点があります。
これにより、支払いプロセスを適切に制御し、セキュリティを確保することができます。

### 平等な報酬分布と直線的な報酬分布

#### 平等なFR支払い

![Total_FR_Payout_Distribution-flat.png](/images/qiita-e7f3b1cd-8ab9-351d-8c0b-410e82c9bc86.webp)
図4: 平等で直線的な報酬配分
引用: https://eips.ethereum.org/EIPS/eip-5173

未来の所有者が利益を得た場合、その利益からの**FR**分配は、すべての対象の所有者に対して平等に行われます。
つまり、すべての所有者は同じ割合で報酬を受け取ります（図4を参照）。

ただし、指数関数的な報酬曲線の方が好ましい場合もあります。
これは、最新の購入者にやや多くの報酬を提供する傾向があります。
また、この分配方法により、初期の世代は**FR**分配が終了に近づくにつれて最も多くの報酬を受け取ります。
つまり、初期の参加者は、初期の段階での参加に対して高い報酬を受け取ることができます。
ただし、この分配方法は、算術的な数列に基づく分配方法ほど極端ではありません（図5を参照）。

このシステムは、どの購入者に対しても差別を行いません。
なぜなら、すべての購入者が同じ分配曲線を経るためです。
つまり、購入者がいつNFTを購入し、どの世代に所属しているかにかかわらず、平等な報酬を受け取ることができます。

#### FR直列配当

![Arithmetic_Sequence_FR_Payout_Distribution.png](/images/qiita-05dad6b9-be12-e1fa-2c23-b340afbd2317.webp)
図5: 等差数列分布
引用: https://eips.ethereum.org/EIPS/eip-5173

利益は、`1、2、3、......`という算術順序に従って分配されます。
最初のオーナーは1人分、2番目のオーナーは2人分、3番目のオーナーは3人分といった具合です。

## 後方互換性

この提案は、現行の**ERC721**標準と**ERC2981**と完全に互換性があります。
また、**ERC1155**との連携にも簡単に対応できます。

## テスト

以下はテストケースを視覚化したものです。

https://eips.ethereum.org/assets/eip-5173/animate-1920x1080-1750-frames.gif?raw=true

## 参考実装

この実装では、OpenZeppelinコントラクトと、固定小数点演算用に**Paul R Berg**が作成した**PRB Math**ライブラリを使用します。
**nFR**標準のインターフェース、**nFR**標準に準拠した拡張を使用した**ERC721**の実装を示します。

```javascript
// SPDX-License-Identifier: CC0-1.0
// Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "./nFR.sol";

contract MyNFT is ERC721URIStorage, Ownable, nFR {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("MyNFT", "NFT") {}

    function mintNFT(address recipient, uint8 numGenerations, uint256 percentOfProfit, uint256 successiveRatio, string memory tokenURI)
        public onlyOwner
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId, numGenerations, percentOfProfit, successiveRatio);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }

    function mintERC721(address recipient, string memory tokenURI) public onlyOwner {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);
    }

    function setDefaultFRInfo(uint8 numGenerations, uint256 percentOfProfit, uint256 successiveRatio) public onlyOwner {
        _setDefaultFRInfo(numGenerations, percentOfProfit, successiveRatio);
    }

    function burnNFT(uint256 tokenId) public onlyOwner {
        _burn(tokenId);
    }

    function _burn(uint256 tokenId) internal override(nFR, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function _transfer(address from, address to, uint256 tokenId) internal override(ERC721, nFR) {
        super._transfer(from, to, tokenId);
    }

    function _mint(address to, uint256 tokenId) internal virtual override(ERC721, nFR) { 
        super._mint(to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, nFR) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }
}
```

引用: https://eips.ethereum.org/assets/eip-5173/Implementation/nFRImplementation.sol

### アーティストやクリエイターへのNFTロイヤルティの分配

アーティストのロイヤルティはブロックチェーン上で行うべきです。
私たちは**ERC2981**NFTロイヤルティ標準提案を支持しています。

全てのプラットフォームは、ブロックチェーンのパラメータと機能に基づいて、同じNFTに対するロイヤルティ報酬をサポートできます。

- 利益がない場合、利益共有やコストは発生しません。
- 「**誰が所有者であったか**」という問題は、コレクションの由来と価値にとって重要です。
- 前の所有者は、その所有に対する補償を受けるべきです。
- **FR**（未来の報酬）はバイヤーや所有者に対するインセンティブであり、ロイヤルティ支払い計画を回避する動機を排除します。

### NFTオーナーの将来報酬（FR）の分布

#### 未来の報酬の計算
NFTが売却された際に実現した利益（P）は、購入者/所有者間で分配されます。
前の所有者は、その利益（P）の一定の部分を受け取り、これが未来の報酬（**FRs**）と呼ばれます。
残りの利益は売り手が受け取ります。

利益分配に参加する前の所有者を決定するために、スライディングウィンドウメカニズムを定義します。
所有者を、最初の所有者から現在の所有者までのキューと考えてみましょう。
利益分配ウィンドウは、前の所有者から現在の所有者まで直ちに始まり、最初の所有者に向かって拡張され、ウィンドウのサイズは固定されています。
ウィンドウ内に位置する前の所有者だけが利益分配に参加します。

![nFR_distribution_formula.png](/images/qiita-77706bd5-08e2-69d8-f59b-9eb74ca176b1.webp)
引用: https://eips.ethereum.org/EIPS/eip-5173

この方程式について詳しく説明します。

- **P**（合計利益）は、NFTが売却された際の売却価格から購入価格を引いた金額を表します。
    - つまり、**P**は売買の差益を示しています。
- **r**（バイヤー報酬比率）は、合計利益**P**の中でバイヤーに割り当てられる割合です。
    - この割合に従って、合計利益が分配されます。
- **g**（共通比）は、幾何学的な連続数列における共通の比率です。
    - この比率は、所有者の世代ごとに報酬がどのように変化するかを示します。
    - 例えば、**g**が`2`の場合、次の所有者の報酬は前の所有者の報酬の2倍になります。
- **n**（実際の参加所有者数）は、未来の報酬共有に資格があり、実際に参加している所有者の数を表します。
    - **n**を計算する際には、現在の所有者数（**m**）と利益分配のスライディングウィンドウアルゴリズムのウィンドウサイズ（**w**）を考慮します。
    - 具体的には、`n = min(m, w)`という式を使って計算されます。
    - この式は、所有者数がウィンドウサイズを超えないように制限するためのものです。

この方程式は、NFTの売買における利益の分配を計算する時に使用されます。
バイヤーに対する報酬比率や、報酬の増加パターンを幾何学的に調整する共通比など、さまざまな要素を考慮しています。
また、実際に報酬を受け取る所有者の数（**n**）もウィンドウサイズによって制限されています。

#### コードへの変換

```javascript
pragma solidity ^0.8.0;
//...

/* Assumes usage of a Fixed Point Arithmetic library (prb-math) for both int256 and uint256, and OpenZeppelin Math utils for Math.min. */
function _calculateFR(uint256 P, uint256 r, uint256 g, uint256 m, uint256 w) pure internal virtual returns(uint256[] memory) {
        uint256 n = Math.min(m, w);
        uint256[] memory FR = new uint256[](n);

        for (uint256 i = 1; i < n + 1; i++) {
            uint256 pi = 0;

            if (successiveRatio != 1e18) {
                int256 v1 = 1e18 - int256(g).powu(n);
                int256 v2 = int256(g).powu(i - 1);
                int256 v3 = int256(P).mul(int256(r));
                int256 v4 = v3.mul(1e18 - int256(g));
                pi = uint256(v4 * v2 / v1);
            } else {
                pi = P.mul(r).div(n);
            }

            FR[i - 1] = pi;
        }

        return FR;
}
```

## セキュリティ考慮事項

### 支払い攻撃
このERCは、ロイヤルティと実現した利益の報酬の収集、分配、および支払いを**ERC721**標準に導入するため、攻撃の可能性が増加します。
フィッシング攻撃を防ぐため、支払いおよび支払い関数全体に再入防止保護を導入することが重要です。
これにより、支払いと支払いにおける最も重要な攻撃ベクトルを軽減できます。

### ロイヤルティ回避
現行の**ERC721**標準では、クリエイターにロイヤルティを支払わないためのさまざまな手法が存在します。
例えば、秘密の取引を通じて新しい購入者のコストベースをゼロにすることができ、これにより彼らの未来の報酬（**FR**）負債が販売価格と同額に増加します。
これは、購入者または売り手、いずれも前の所有者の実現した利益（`P x r`）の一部を支払うことを意味します。
購入者は自身の利益を追求し、ロイヤルティ回避の提案を拒否します。

### ウォッシュセールによるFR蓄積
**Quantexa**ブログと**beincrypto**の記事によれば、規制のない仮想通貨取引プラットフォームとNFTマーケットプレイスでは、広範なウォッシュトレードが行われています。
不正行為者によるウォッシュトレードの使用は、不正な優位性、価格の膨張、および資金洗浄をもたらす可能性があります。
単一のエンティティが未来の報酬を蓄積するために複数の所有者になる場合、システムの妥当性が損なわれる可能性があります。

#### ユーザーによるウォッシュトレード

攻撃者が異なるウォレットアドレスを使用して、NFTを自分自身に損失を出す形で「**売却**」することを指します。
これにより、攻撃者はそのプロセスを何度も繰り返すことで、将来の報酬（**FR**）分配の割合を最大化しようとします（図6参照）。
このような行為を防ぐために、ウォレットのランキングスコアが使用されることがあります。
新しいウォレットは、その信頼性が低いことを示す兆候であり、取引履歴が短い（一定数の取引が行われていない）場合、マーケットプレイスはそのウォレットからの**FR**分配を制限することがあります。

我々は、将来の報酬の大部分が少数のウォッシュトレーダーに集中しないようにしたいと考えています。
このような行為を少なくし、ウォッシュトレーディングや報酬の過度な蓄積を防ぐ方法の一つは、それらの行為が利益を上げにくいものにすることです。
具体的な対策として、ウォレットのスコアと保有期間に基づくインセンティブシステムを導入することが考えられます。
新しいウォレットが使用された場合や、保有期間が一定期間未満の場合、両当事者の報酬が削減されるようなメカニズムです。

![Same_owner_using_different_wallets.jpeg](/images/qiita-6ee956c2-4e45-5698-fdee-0baa9e05b633.webp)
図6: 同じ所有者が異なるウォレットを使用
引用: https://eips.ethereum.org/EIPS/eip-5173

#### マーケットプレイス運営者によるウォッシュ取引

一方、最も問題となるのはマーケットプレイス自体です。
**Decrypt**によれば、これらのマーケットプレイスはウォッシュトレードに大きく関与したり、またはそれに無関心だったりすることがよくあります。
ERCの筆者自身もこの現象を経験しているそうです。
あるトップ5の仮想通貨取引所の高位幹部は、2018年の深夜の飲み会で、新しくリストアップされたトークンを「**マーケットメイキング**」と呼びながら、「**ブラッシュ**」（ウォッシュトレード）したことを自慢していました。
それにもかかわらず、その取引所は今でもトップ5の仮想通貨取引所の中にランクインしています。

これらの企業の多くは、自己取引を行ったり、特定のユーザーと共謀してロイヤルティと未来の報酬支払いを裏で行ったりしています。
自己取引を防ぐために、すべての取引所には堅牢な機能が必要です。
ユーザーは透明性を持ってウォッチャーを監視できるべきです。
また、マーケットプレイスは、**Chainalysis Reactor**のようなオンチェーントランザクションモニタリングサービスへの無料アクセスを顧客に提供すべきです。

### ロング／サイクルFRオーナー
悪意のある行為者は、ほとんどの場合、非常に長いまたは循環的な未来の報酬所有者の世代を作成します。
これにより、**FR**を分配しようとするアプリケーションがガスを使い果たし、正常に動作しなくなる可能性があります。
したがって、クライアントは、やり取りするコントラクトが適切な世代数を持っていることを確認する責任があります。
これにより、ループ処理がガスを使い切ることを防ぎます。

## 引用

Yale ReiSoleil (@longnshort), dRadiant (@dRadiant), D Wang, PhD <david@iob.fi>, "ERC-5173: NFT Future Rewards (nFR) [DRAFT]," Ethereum Improvement Proposals, no. 5173, May 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5173.

## 最後に

今回は「非代替性トークン（NFT）の過去の所有者全員に報酬を与える多世代報酬メカニズムを提案している規格である**ERC5173**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58


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