# [ERC5585] NFTに紐づくさまざまな権利を管理する仕組みを理解しよう！

> この提案は、ERC721規格のNFT（非代替性トークン）に関連しています。通常、NFTは特定のデジタルアセットやコンテンツを所有する権利を表します。

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

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC721**規格のNFTの所有権と利用権などのさまざまな権利を分離する提案である**ERC5585**についてまとめていきます！

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

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

:::message alert
**5585**は現在（2023年9月7日）では「**Review**」段階です。
:::

## 概要

この提案は、**ERC721**規格のNFT（非代替性トークン）に関連しています。
通常、NFTは特定のデジタルアセットやコンテンツを所有する権利を表します。

この提案は、NFTの所有権と、そのデジタルアセットを商業的に利用する権利を分けることを提案しています。
つまり、NFTを持っている人と、そのデジタルアセットを使ってビジネスを行うための権利を持っている人を別々に管理できるようにします。

これがなぜ重要かというと、NFTの所有者がアート作品や他のデジタルコンテンツを商業的に利用しない場合でも、それらの商用利用権を他の人や企業に売却したり、ライセンスを提供したりできるようになるからです。
つまり、NFTの所有者と商用利用権の管理者が別々の人であっても、お互いの権利を独立して扱えるようになります。

この提案により、NFTの使い方がより柔軟になり、アーティストやコンテンツクリエイターにとって新たな収益の機会が生まれる可能性があります。

## 動機

通常、NFT（不換トークン）は、デジタルアセットの所有権を単一の所有者に限定するシステムを採用しています。
つまり、あるNFTを持っている人は、そのデジタルアセットの唯一の所有者として認識されます。
このモデルでは、他の人にそのデジタルアセットに関連する権利を譲渡することが難しい、たとえばデジタルアートのNFTを持っている場合、他の人にそのデジタルアートを表示したり、派生作品を作成したり、配布したりする権利を付与することができません。
これにより、NFTの商業的な価値と活用が制限されています。

しかし、この提案は、NFTの所有権とデジタルアセットを商業的に利用するための権利を分けることを提案しています。
つまり、NFTの所有者と、そのNFTに関連する権利（商用利用権、表示権、派生作品の作成権、配布権など）を独立して管理できるようにすることを目指しています。
これにより、NFTの所有者は、自分が所有するデジタルアセットを商業的に利用しない場合でも、他の人や企業にそれらの権利を提供したり、売却したりすることが可能になります。
例えば、アーティストが自分のデジタルアートのNFTを持っている場合、他の人にそのアートを商業的に利用する権利をライセンスとして提供することができます。

商用権には、著作権に基づくさまざまな権利が含まれます。
これにはコピー権、表示権、配布権、貸与権、商用利用権、変更権、複製権、サブライセンス権などが含まれます。
メタバースの発展に伴い、NFTはデジタルコレクション、バーチャル不動産、音楽、アート、ソーシャルメディアなど、さまざまな用途に活用されています。
NFTの所有権と商用権を分けることで、これらの新しい用途において著作権や認可に基づいたビジネスモデルが展開される可能性が高まっています。

## 仕様

### コントラクトインターフェース

```javascript
interface IERC5585 {

    struct UserRecord {
        address user;
        string[] rights;
        uint256 expires
    }

    /// @notice Get all available rights of this NFT project
    /// @return All the rights that can be authorized to the user
    function getRights() external view returns(string[]);

    /// @notice NFT holder authorizes all the rights of the NFT to a user for a specified period of time
    /// @dev The zero address indicates there is no user
    /// @param tokenId The NFT which is authorized
    /// @param user The user to whom the NFT is authorized
    /// @param duration The period of time the authorization lasts
    function authorizeUser(uint256 tokenId, address user, uint duration) external;

    /// @notice NFT holder authorizes specific rights to a user for a specified period of time
    /// @dev The zero address indicates there is no user. It will throw exception when the rights are not defined by this NFT project
    /// @param tokenId The NFT which is authorized
    /// @param user The user to whom the NFT is authorized
    /// @param rights Rights autorised to the user, such as renting, distribution or display etc
    /// @param duration The period of time the authorization lasts
    function authorizeUser(uint256 tokenId, address user, string[] rights, uint duration) external;
    
    /// @notice The user of the NFT transfers his rights to the new user
    /// @dev The zero address indicates there is no user
    /// @param tokenId The rights of this NFT is transferred to the new user
    /// @param newUser The new user
    function transferUserRights(uint256 tokenId, address newUser) external;

    /// @notice NFT holder extends the duration of authorization
    /// @dev The zero address indicates there is no user. It will throw exception when the rights are not defined by this NFT project
    /// @param tokenId The NFT which has been authorized
    /// @param user The user to whom the NFT has been authorized
    /// @param duration The new duration of the authorization
    function extendDuration(uint256 tokenId, address user, uint duration) external;

    /// @notice NFT holder updates the rights of authorization
    /// @dev The zero address indicates there is no user
    /// @param tokenId The NFT which has been authorized
    /// @param user The user to whom the NFT has been authorized
    /// @param rights New rights autorised to the user
    function updateUserRights(uint256 tokenId, address user, string[] rights) external;

    /// @notice Get the authorization expired time of the specified NFT and user
    /// @dev The zero address indicates there is no user
    /// @param tokenId The NFT to get the user expires for
    /// @param user The user who has been authorized
    /// @return The authorization expired time
    function getExpires(uint256 tokenId, address user) external view returns(uint);

    /// @notice Get the rights of the specified NFT and user
    /// @dev The zero address indicates there is no user
    /// @param tokenId The NFT to get the rights
    /// @param user The user who has been authorized
    /// @return The rights has been authorized
    function getUserRights(uint256 tokenId, address user) external view returns(string[]);

    /// @notice The contract owner can update the number of users that can be authorized per NFT
    /// @param userLimit The number of users set by operators only
    function updateUserLimit(unit256 userLimit) external onlyOwner;

    /// @notice resetAllowed flag can be updated by contract owner to control whether the authorization can be revoked or not 
    /// @param resetAllowed It is the boolean flag
    function updateResetAllowed(bool resetAllowed) external onlyOwner;

    /// @notice Check if the token is available for authorization
    /// @dev Throws if tokenId is not a valid NFT
    /// @param tokenId The NFT to be checked the availability
    /// @return true or false whether the NFT is available for authorization or not
    function checkAuthorizationAvailability(uint256 tokenId) public view returns(bool);

    /// @notice Clear authorization of a specified user
    /// @dev The zero address indicates there is no user. The function  works when resetAllowed is true and it will throw exception when false  
    /// @param tokenId The NFT on which the authorization based
    /// @param user The user whose authorization will be cleared
    function resetUser(uint256 tokenId, address user) external;


    /// @notice Emitted when the user of a NFT is changed or the authorization expires time is updated
    /// param tokenId The NFT on which the authorization based
    /// param indexed user The user to whom the NFT authorized
    /// @param rights Rights autorised to the user
    /// param expires The expires time of the authorization
    event authorizeUser(uint256 indexed tokenId, address indexed user, string[] rights, uint expires);

    /// @notice Emitted when the number of users that can be authorized per NFT is updated
    /// @param userLimit The number of users set by operators only
    event updateUserLimit(unit256 userLimit);
}
```

1. `getRights()`関数は、権利情報を取得できます。
2. `authorizeUser(uint256 tokenId, address user, uint duration)` 関数は、NFTのユーザーに対して権限を与えます。
3. `authorizeUser(uint256 tokenId, address user, string[] rights; uint duration)` 関数は、同様にユーザーに対する権限を与えるためのものですが、権限の種類を文字列の配列で指定できるバージョンです。
4. `transferUserRights(uint256 tokenId, address newUser)` 関数は、ユーザーの権限を別のユーザーに転送できます。
5. `extendDuration(uint256 tokenId, address user, uint duration)` 関数は、ユーザーの権限の有効期限を延長します。
6. `updateUserRights(uint256 tokenId, address user, string[] rights)` 関数は、ユーザーの権利を更新します。
7. `getExpires(uint256 tokenId, address user)` 関数は、ユーザーの権限の有効期限を取得します。
8. `getUserRights(uint256 tokenId, address user)` 関数は、ユーザーの権利情報を取得します。
9. `updateUserLimit(unit256 userLimit)` 関数は、NFTごとに許可されるユーザー数を更新します。
10. `updateResetAllowed(bool resetAllowed)` 関数は、リセット操作が許可されるかどうかを更新します。
11. `checkAuthorizationAvailability(uint256 tokenId)` 関数は、指定したNFTの権限の利用可能性を確認します。
12. `authorizeUser`イベントは、NFTのユーザーが変更されたり、権限の有効期限が更新された時に発行されます。
13. `updateUserLimit` イベントは、NFTごとに許可されるユーザー数が更新された時に発行されます。

#### UserRecord

```javascript
struct UserRecord {
    address user;
    string[] rights;
    uint256 expires;
}
```

**概要**
**ERC721**NFTのユーザー情報を格納するために使用されるデータ構造。

**パラメータ**
- `user`
    - ユーザーのEthereumアドレス。
- `rights`
    - ユーザーに付与された権限（例：コピー権、表示権、配布権など）が文字列のリスト。
    - ユーザーがどの権限を持っているかを示します。
- `expires`
    - 権限の有効期限を表す整数値。
    - UNIXタイムスタンプ（エポックからの秒数）として表現され、特定の日時まで権限が有効であることを示します。

---

#### authorizeUser

```javascript
event authorizeUser(uint256 indexed tokenId, address indexed user, string[] rights, uint expires);
```

**概要**
**ERC721**NFTのユーザーが変更された場合や権限の有効期限が更新された時に発行されるイベント。

**パラメータ**
- `tokenId`
    - 対象となる**ERC721**NFTのトークンID。
- `user`
    - 権限を受け取るユーザーのEthereumアドレス。
    - どのユーザーが特定のNFTに対してどの権限を持つかを示します。
- `rights`
    - ユーザーに付与された権限のリスト。
    - 例えば、コピー権、表示権、配布権などの権限が含まれます。
- `expires`
    - 権限の有効期限を示す整数値。
    - UNIXタイムスタンプ形式で、権限が有効である期間を表します。

#### updateUserLimit

```javascript
event updateUserLimit(unit256 userLimit);
```

**概要**
各**ERC721**NFTごとに許可されるユーザー数が更新された時に発行されるイベント。

**パラメータ**
- `userLimit`
    - **ERC721**NFTごとに許可されるユーザー数の制限が設定された整数値。

---

#### getRights

```javascript
function getRights() external view returns(string[]);
```

**概要**
NFTプロジェクトで利用可能なすべての権利を取得する関数。

**詳細**
ユーザーに許可できるすべての権利を返します。
これには、レンタル、配信、表示などの権利が含まれます。

**戻り値**
stringの配列で、ユーザーが許可できるすべての権利が含まれています。

---

#### authorizeUser

```javascript
function authorizeUser(uint256 tokenId, address user, uint duration) external;
```

**概要**
NFTの所有者が指定した期間の間、指定したユーザーに対してNFTのすべての権利を許可する関数。

**詳細**
NFTの所有者は、指定したNFTに対して、指定した期間（`duration`）の間、指定したユーザーアドレス（`user`）に対してすべての権利を許可します。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 許可するNFTのトークンID。
- `user`
    - 権限を付与するユーザーのアドレス。
- `duration`
    - 許可期間（秒単位）。

---

#### authorizeUser

```javascript
function authorizeUser(uint256 tokenId, address user, string[] rights, uint duration) external;
```

**概要**
NFTの所有者が指定した期間の間、指定したユーザーに対して特定の権利を許可する関数。

**詳細**
NFTの所有者は、指定したNFTに対して、指定した期間（`duration`）の間、指定したユーザーアドレス（`user`）に対して特定の権利（`rights`）を許可します。
権利がこのNFTプロジェクトで定義されていない場合、例外が投げられます。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 許可するNFTのトークンID。
- `user`
    - 権限を付与するユーザーのアドレス。
- `rights`
    - 許可する特定の権利の文字列配列。
- `duration`
    - 許可期間（秒単位）。

---

#### transferUserRights

```javascript
function transferUserRights(uint256 tokenId, address newUser) external;
```

**概要**
NFTのユーザーが権利を新しいユーザーに移転する関数。

**詳細**
NFTのユーザーは、指定したNFTの権利を新しいユーザーアドレス（`newUser`）に移転します。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 権利を移転するNFTのトークンID。
- `newUser`
    - 権利を受け継ぐ新しいユーザーのアドレス。

---

#### extendDuration

```javascript
function extendDuration(uint256 tokenId, address user, uint duration) external;
```

**概要**
NFTの所有者が許可期間を延長する関数。

**詳細**
NFTの所有者は、指定したNFTに対して特定のユーザー（`user`）の許可期間を新しい期間（`duration`）に延長します。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 許可期間を延長するNFTのトークンID。
- `user`
    - 許可期間を延長するユーザーのアドレス。
- `duration`
    - 新しい許可期間（秒単位）。

---

#### updateUserRights

```javascript
function updateUserRights(uint256 tokenId, address user, string[] rights) external;
```

**概要**
NFTの所有者が特定のユーザーに対する権利を更新する関数。

**詳細**
NFTの所有者は、指定したNFTに対して特定のユーザー（`user`）の権利を新しい権利（`rights`）に更新します。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 権利を更新するNFTのトークンID。
- `user`
    - 権利を更新するユーザーのアドレス。
- `rights`
    - 新しい権利の文字列配列。

---

#### getExpires

```javascript
function getExpires(uint256 tokenId, address user) external view returns(uint);
```

**概要**
指定したNFTとユーザーの認証期限を取得する関数。

**詳細**
NFTの所有者は、指定したNFTとユーザー（`user`）の認証が失効するまでの時間（秒単位）を取得します。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 認証期限を取得するNFTのトークンID。
- `user`
    - 認証期限を取得するユーザーのアドレス。

**戻り値**
認証が失効するまでの時間（秒単位）を表す整数を返します。

---

#### getUserRights

```javascript
function getUserRights(uint256 tokenId, address user) external view returns(string[]);
```

**概要**
指定したNFTとユーザーに対する権利を取得する関数。

**詳細**
NFTの所有者は、指定したNFTとユーザー（`user`）に対する許可された権利の文字列配列を取得します。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 権利を取得するNFTのトークンID。
- `user`
    - 権利を取得するユーザーのアドレス。

**戻り値**
許可された権利の文字列配列を返します。

---

#### updateUserLimit

```javascript
function updateUserLimit(uint256 userLimit) external onlyOwner;
```

**概要**
コントラクトの所有者がNFTごとに許可できるユーザー数を更新する関数。

**詳細**
契約の所有者は、NFTごとに許可できるユーザー数を新しい値（`userLimit`）に更新します。

**引数**
- `userLimit`
    - オペレーターが設定するユーザー数の制限。

---

#### updateResetAllowed

```javascript
function updateResetAllowed(bool resetAllowed) external onlyOwner;
```

**概要**
コントラクトの所有者が認証を取り消し可能かどうかを制御するために`resetAllowed`フラグを更新する関数。

**詳細**
契約の所有者は、`resetAllowed`フラグを設定して、認証の取り消しを許可または禁止できます。
`resetAllowed`が`true`の場合、認証の取り消しが可能です。
`resetAllowed`が`false`の場合、認証の取り消しは許可されません。

**引数**
- `resetAllowed`
    - 認証の取り消しを許可または禁止するブール値。

---

#### checkAuthorizationAvailability

```javascript
function checkAuthorizationAvailability(uint256 tokenId) public view returns(bool);
```

**概要**
指定したトークンIDのNFTが認証可能かどうかを確認する関数。

**詳細**
指定したトークンIDのNFTが認証可能かどうかを確認します。
トークンが存在しない場合や無効なNFTの場合、例外が投げられます。

**引数**
- `tokenId`
    - 認証可能かどうかを確認するNFTのトークンID。

**戻り値**
NFTが認証可能であれば`true`、それ以外の場合は`false`を返します。

---

#### resetUser

```javascript
function resetUser(uint256 tokenId, address user) external;
```

**概要**
指定したNFTとユーザーの認証をリセットする関数。

**詳細**
コントラクトの所有者が指定したNFTとユーザーの認証をリセットします。
`resetAllowed`が`false`の場合、例外が投げられます。
ユーザーアドレスが`0`の場合、ユーザーが存在しないことを示します。

**引数**
- `tokenId`
    - 認証をリセットするNFTのトークンID。
- `user`
    - リセットする認証を持つユーザーのアドレス。


## 補足

#### 最大許可ユーザー数とキャンセル機能
この提案ではNFTのコントラクトオーナーが各NFTに対して最大許可ユーザー数を設定できるようになります。
これにより、NFTの所有者は自身のデジタルアセットを共有するユーザーの数を制限できます。
また、所有者は必要に応じてユーザーの許可をいつでも取り消すことができます。
これにより、デジタルアセットの権限管理が柔軟に行え、関係者の利益を保護できます。

#### resetAllowed
`resetAllowed`フラグが導入されています。
このフラグが`true`に設定されている場合、NFTの所有者はいつでもすべての許可ユーザーの使用権を無効にできます。
これはコントラクトでのユーザー間の権利の管理を効果的に制御する手段となります。
たとえば、緊急の状況やセキュリティ上の理由から、NFTの所有者は一括して権限を取り消すことができます。

#### ユーザーレコード内の権利
ユーザーレコード構造は、NFTの所有者が特定の権利を持つユーザーに許可を与えるために使用されます。
つまり、NFTの所有者は個々のユーザーに対して、例えばコピー権、表示権、配布権などの特定の権利を付与し、必要に応じてそれを更新できます。
これにより、個別のユーザーに対して異なる権利を管理できます。

#### 第三者とのシームレスな統合
この提案は**ERC721**の拡張として設計されており、他のNFTプロジェクトとシームレスに統合できます。
これは、新しいNFTプロジェクトに簡単にこの機能を追加できることを意味します。
他のプロジェクトは、これらの提供されるインターフェースと関数を活用して、独自のトランザクションや機能を実装できます。
例えば、アナウンスメントプラットフォームは、このEIPを使用して、NFTの所有者がいつでも許可または非許可を行えるようにすることができ、新しい用途を実現できます。

## 後方互換性

この規格は**ERC721**を拡張したもので互換性があります。

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

`resetAllowed`フラグが`false`の場合、つまりNFTの所有者が許可を取り消せない期間中、ユーザーはNFTが新しい所有者に売却された場合に手数料を公平に割り当てる方法を確保する必要があります。

以下はそのための具体的な方法です。
ユーザーが支払った手数料は、許可の期間に応じて一時的にコントラクトに保持されます。
たとえば、許可の期間が12ヶ月で合計の料金が`10ETH`の場合、NFTが3ヶ月後に新しい所有者に転送されたと仮定します。
この場合、元の支払った料金のうち、転送後の期間（3ヶ月）に相当する部分、つまり`2.5ETH`だけが新しい所有者に送金され、残りの`7.5ETH`は元のユーザーに返金されます。

この仕組みにより、NFTの所有権が移転した場合でも、過去の許可料金が公平に分配され、元のユーザーに返金されることが確保されます。
これにより、ユーザーと新しいNFT所有者の権益が保護され、公平な取引が促進されます。

## 引用

Veega Labs (@VeegaLabsOfficial), Sean NG (@ngveega), Tiger (@tiger0x), Fred (@apan), Fov Cao (@fovcao), "ERC-5585: ERC-721 NFT Authorization [DRAFT]," Ethereum Improvement Proposals, no. 5585, August 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5585.

## 最後に

今回は「**ERC721**規格のNFTの所有権と利用権などのさまざまな権利を分離する提案である**ERC5585**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58
