[ERC7432] NFTのさまざまなロールを管理する仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、NFTのさまざまなロールを管理するインターフェースを提案している**ERC7432**についてまとめていきます！

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

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

:::note warn
**7432**は現在（2023年9月7日）では「**Review**」段階です。
:::

# 概要

この規格は、NFTに特定のロールを与える仕組みを提案しています。
NFTごとにロールが紐づけられており、あらかじめ設定された時間が経過すると紐づけられたロールは自動的に無効になります。
ここでの「**ロール**」とは、特定の機能や権限を指すもので、たとえば「**管理者**」や「**所有者**」といったものです。
これらのロールは、情報をバイト（`bytes32`）の形式で保持し、さらにカスタムの「`_data`」フィールドを持っています。
この「`_data`」フィールドは、ロールに関連付けられた情報を自由に格納でき、カスタマイズすることが可能です。

# 動機

このインターフェースは、NFT（非代替トークン）におけるロール管理のための標準の提供を目指しています。
この標準に従うことで、分散型アプリケーション（dApps）は、特権的な操作を実行するためのアクセス制御を実現できます。
たとえば、トークンの発行（エアドロップの権利など）を特定のユーザーに制限することができます。

NFTのロール管理は、dAppsとシームレスに統合され、ユーティリティ（機能や権限）を共有するメカニズムを提供します。
具体的な例として、デジタル不動産を考えてみましょう。
ユーザーはデジタルの不動産NFTを作成し、他のユーザーに「`PROPERTY_MANAGER`」というロールを与えることができます。
これにより、所有権を譲渡することなく、特定の機能や権限を委任できます。
同じユーザーは「`PROPERTY_TENANT`」というロールを複数のユーザーに与えることもでき、これによりロールを付与されたアドレスはデジタル不動産にアクセスし、対話することができます。

また、分散型金融（DeFi）においても興味深い活用事例があります。
保険ポリシーをNFTとして発行し、受益者、被保険者、保険会社などをブロックチェーン上で追跡することができます。
これにより、契約者や関係者のロールを透明かつ信頼性の高い方法で管理できます。

# 仕様

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

```javascript
/// @title ERC-7432 Non-Fungible Token Roles
/// @dev See https://eips.ethereum.org/EIPS/eip-7432
/// Note: the ERC-165 identifier for this interface is 0xd7e151ef.
interface IERC7432 /* is ERC165 */ {
  
    /** Events **/

    /// @notice Emitted when a role is granted.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantor The user assigning the role.
    /// @param _grantee The user receiving the role.
    /// @param _expirationDate The expiration date of the role.
    /// @param _data Any additional data about the role.
    event RoleGranted(
        bytes32 indexed _role,
        address indexed _tokenAddress,
        uint256 indexed _tokenId,
        address _grantor,
        address _grantee,
        uint64 _expirationDate,
        bytes _data
    );

    /// @notice Emitted when a role is revoked.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _revoker The user revoking the role.
    /// @param _grantee The user that receives the role revocation.
    event RoleRevoked(
        bytes32 indexed _role,
        address indexed _tokenAddress,
        uint256 indexed _tokenId,
        address _revoker,
        address _grantee
    );

    /// @notice Emitted when a user is approved to manage any role on behalf of another user.
    /// @param _tokenAddress The token address.
    /// @param _operator The user approved to grant and revoke roles.
    /// @param _isApproved The approval status.
    event RoleApprovalForAll(
        address indexed _tokenAddress,
        address indexed _operator,
        bool _isApproved
    );

    /// @notice Emitted when a user is approved to manage the roles of an NFT on behalf of another user.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _operator The user approved to grant and revoke roles.
    /// @param _isApproved The approval status.
    event RoleApproval(
        address indexed _tokenAddress,
        uint256 indexed _tokenId,
        address _operator,
        bool _isApproved
    );

    /** External Functions **/ 

    /// @notice Grants a role to a user.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantee The user receiving the role.
    /// @param _expirationDate The expiration date of the role.
    /// @param _data Any additional data about the role.
    function grantRole(
        bytes32 _role,
        address _tokenAddress,
        uint256 _tokenId,
        address _grantee,
        uint64 _expirationDate,
        bytes calldata _data
    ) external;

    /// @notice Revokes a role from a user.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantee The user that receives the role revocation.
    function revokeRole(
        bytes32 _role,
        address _tokenAddress,
        uint256 _tokenId,
        address _grantee
    ) external;

    /// @notice Grants a role on behalf of a user.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantor The user assigning the role.
    /// @param _grantee The user that receives the role.
    /// @param _expirationDate The expiration date of the role.
    /// @param _data Any additional data about the role.
    function grantRoleFrom(
        bytes32 _role,
        address _tokenAddress,
        uint256 _tokenId,
        address _grantor,
        address _grantee,
        uint64 _expirationDate,
        bytes calldata _data
    ) external;

    /// @notice Revokes a role on behalf of a user.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _revoker The user revoking the role.
    /// @param _grantee The user that receives the role revocation.
    function revokeRoleFrom(
        bytes32 _role,
        address _tokenAddress,
        uint256 _tokenId,
        address _revoker,
        address _grantee
    ) external;

    /// @notice Approves operator to grant and revoke any roles on behalf of another user.
    /// @param _tokenAddress The token address.
    /// @param _operator The user approved to grant and revoke roles.
    /// @param _approved The approval status.
    function setRoleApprovalForAll(
        address _tokenAddress,
        address _operator,
        bool _approved
    ) external;

    /// @notice Approves operator to grant and revoke roles of an NFT on behalf of another user.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _operator The user approved to grant and revoke roles.
    /// @param _approved The approval status.
    function approveRole(
        address _tokenAddress,
        uint256 _tokenId,
        address _operator,
        bool _approved
    ) external;

    /** View Functions **/

    /// @notice Checks if a user has a role.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantor The user that assigned the role.
    /// @param _grantee The user that received the role.
    function hasRole(
      bytes32 _role,
      address _tokenAddress,
      uint256 _tokenId,
      address _grantor,
      address _grantee
    ) external view returns (bool);
  
    /// @notice Checks if a user has a unique role.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantor The user that assigned the role.
    /// @param _grantee The user that received the role.
    function hasUniqueRole(
      bytes32 _role,
      address _tokenAddress,
      uint256 _tokenId,
      address _grantor,
      address _grantee
    ) external view returns (bool);
  
    /// @notice Returns the custom data of a role assignment.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantor The user that assigned the role.
    /// @param _grantee The user that received the role.
    function roleData(
      bytes32 _role,
      address _tokenAddress,
      uint256 _tokenId,
      address _grantor,
      address _grantee
    ) external view returns (bytes memory data_);
  
    /// @notice Returns the expiration date of a role assignment.
    /// @param _role The role identifier.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantor The user that assigned the role.
    /// @param _grantee The user that received the role.
    function roleExpirationDate(
      bytes32 _role,
      address _tokenAddress,
      uint256 _tokenId,
      address _grantor,
      address _grantee
    ) external view returns (uint64 expirationDate_);

    /// @notice Checks if the grantor approved the operator for all NFTs.
    /// @param _tokenAddress The token address.
    /// @param _grantor The user that approved the operator.
    /// @param _operator The user that can grant and revoke roles.
    function isRoleApprovedForAll(
      address _tokenAddress,
      address _grantor,
      address _operator
    ) external view returns (bool);

    /// @notice Checks if the grantor approved the operator for a specific NFT.
    /// @param _tokenAddress The token address.
    /// @param _tokenId The token identifier.
    /// @param _grantor The user that approved the operator.
    /// @param _operator The user approved to grant and revoke roles.
    function getApprovedRole(
        address _tokenAddress,
        uint256 _tokenId,
        address _grantor,
        address _operator
    ) external view returns (bool);

}
```

## メタデータ拡張

NFT（非代替トークン）の従来のJSONベースのメタデータスキーマを拡張します。
そのため、この機能をサポートするDApps（分散型アプリケーション）は、必ず**ERC721**または**ERC1155**のメタデータ拡張を実装する必要があります。
この拡張機能はオプションであり、開発者がロールに関する追加情報を提供できるようにします。

:::note info
1. **Roles Metadata拡張**
NFTに関連付けられたロールに関する追加情報を提供するための機能です。
例えば、特定のNFTが「`PROPERTY_MANAGER`」の役割を持っている場合、その「`PROPERTY_MANAGER`」の詳細情報を提供することができます。

2. **JSONベースのメタデータスキーマ**
通常、NFTにはJSON形式のメタデータが含まれており、それにはトークンの名前、説明、画像URLなどが含まれています。
**Roles Metadata**拡張は、この既存のメタデータスキーマを拡張して、ロールに関する情報を追加できるようにします。

3. **DAppsのサポート**
**Roles Metadata**拡張をサポートするDAppsは、NFTのメタデータにロール情報を正しく表示し、操作できる必要があります。
ただし、この機能を実装する際には、**ERC721**または**ERC1155**のメタデータ拡張も同時に実装しなければなりません。
:::

アップデートされたメタデータスキーマ。

```json
{
  
  /** Existing NFT Metadata **/

  "title": "Asset Metadata",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Identifies the asset to which this NFT represents"
    },
    "description": {
      "type": "string",
      "description": "Describes the asset to which this NFT represents"
    },
    "image": {
      "type": "string",
      "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive"
    }
  },
  
  /** Additional fields for Roles **/

  "roles": [
    {
      "id": {
        "type": "bytes32",
        "description": "Identifies the role"
      },
      "name": {
        "type": "string",
        "description": "Human-readable name of the role"
      },
      "description": {
        "type": "string",
        "description": "Describes the role"
      },
      "isUniqueRole": {
        "type": "boolean",
        "description": "Whether the role supports simultaneous assignments or not"
      },
      "inputs": [
        {
          "name": {
            "type": "string",
            "description": "Human-readable name of the argument"
          },
          "type": {
            "type": "string",
            "description": "Solidity type, e.g., uint256 or address"
          }
        }
      ]
    }
  ]
  
}
```

以下のJSONは**ERC7432**メタデータの例です。

```json
{
  // ... Existing NFT Metadata
  
  "roles": [
    {
      // keccak256("PROPERTY_MANAGER")
      "id": "0x5cefc88e2d50f91b66109b6bb76803f11168ca3d1cee10cbafe864e4749970c7",
      "name": "Property Manager",
      "description": "The manager of the property is responsible for furnishing it and ensuring its good condition.",
      "isUniqueRole": false,
      "inputs": []
    },
    {
      // keccak256("PROPERTY_TENANT")
      "id": "0x06a3b33b0a800805559ee9c64f55afd8a43a05f8472feb6f6b77484ff5ac9c26",
      "name": "Property Tenant",
      "description": "The tenant of the property is responsible for paying the rent and keeping the property in good condition.",
      "isUniqueRole": true,
      "inputs": [
        {
          "name": "rent",
          "type": "uint256"
        }
      ]
    }
  ]
  
}
```

1. **ロールの配列プロパティ**
これらのプロパティは、NFTの役割に関する情報を含むものです。
開発者は必要に応じて、ロールについての追加情報（たとえば、ロールの画像や詳細な説明）を追加できます。
しかし、「`isUniqueRole`」というプロパティを含めることが非常に重要です。
この「`isUniqueRole`」フィールドは、あるロールが一意であるかどうかを示し、それに応じて「`hasRole`」または「`hasUniqueRole`」という関数を呼び出すかどうかを判断するのに役立ちます。

2. **inputsプロパティの重要性**
このプロパティは、特定の操作（たとえば、ロールの付与）に必要な情報を記述する場所です。
具体的には、どのパラメーターをエンコードし、`grantRole`関数に渡すかを示します。
このとき、「`inputs`」プロパティには、パラメーターの型を示す「`type`」と、複雑なデータ構造（たとえば、タプル型）の場合に使用される「`components`」が含まれます。
これにより、操作が正確に実行されるための情報が提供されます。

# 注意事項

1. **IERC7432インターフェースの実装**
この規格に準拠したコントラクトは、**IERC7432**インターフェースを実装する必要があります。
これにより、ロール管理の標準化が促進されます。

2. **ロールの表現**
ロールは`bytes32`型で表されます。
通常、ロール名は`keccak256`ハッシュ関数を使用してハッシュ化され、このハッシュがロールを表します。
例えば、`ROLE_NAME`ロールのハッシュは`keccak256("ROLE_NAME")`です。

3. **grantRole関数**
ロールを付与する関数で、`_expirationDate`（有効期限）が過去の場合、エラーを発生させる必要があります。

4. **grantRoleFrom関数**
`_expirationDate`が過去の場合、または`msg.sender`が`_grantor`の代わりにロールを付与する権限を持っていない場合、エラーを発生させる必要があります。

5. **revokeRole関数**
ロールを取り消す関数です。

6. **revokeRoleFrom関数**
`msg.sender`が`_revoker`の代わりにロールを取り消す権限を持っていない場合、エラーを発生させる必要があります。

7. **setRoleApprovalForAll関数**
ロールの付与権限を設定する関数です。

8. **approveRole関数**
ロールの承認権限を設定する関数です。

9. **hasRole関数**
ロールの割り当てが存在し、有効期限が切れていないかどうかを確認する関数です。

10. **hasUniqueRole関数**
ロールの割り当てが存在し、有効期限が切れておらず、最後に付与されたロールであるかどうかを確認する関数で、pureまたはviewで実装さ、同時にロールの割り当てがサポートされません。

11. **roleData関数**
ロールの割り当てが存在しない場合、空のバイト配列を返します。

12. **roleExpirationDate関数**
ロールの割り当てが存在しない場合、`0`ゼロを返します。

13. **isRoleApprovedForAll関数**
指定した`_operator`が`_grantor`の代わりにすべてのNFTに対してロールを付与および取り消す権限を持っている場合、`true`を返します。

14. **getApprovedRole関数**
指定した`_operator`が`_grantor`の指定したNFTに対してロールを付与および取り消す権限を持っている場合、`true`を返します。

15. **ERC165のサポート**
この規格に準拠したコントラクトは**ERC165**をサポートします。

# 補足

**ERC7432**は**ERC721**や**ERC1155**の拡張ではありません。
**ERC7432**は、特定のNFT実装に依存せず、どのNFTプラットフォームでも使えるように設計されています。
これにより、標準を外部で実装するか、NFTのコントラクト内で実装し、dAppsが不変のNFTとロールを組み合わせて使えます。

## 自動有効期限
自動有効期限は、ロールの付与とロールの有効性のチェックに関わります。
ロールの付与において、有効期限を設定するのが`grantRole`関数の役割で、ロールの有効性を確認するのが`hasRole`関数の役割です。
日付は`uint256`ではなく、`uint64`で表されます。
最大の`UNIX`タイムスタンプは約`584,942,417,355`年で、これは「**永久**」と考えられるため、期限切れを設定しないケースには`type(uint64).max`を使用することが推奨されています。

## ユニークとユニークでないロール
この標準は、ユニークなロール（一度に1つのアカウントに割り当てられる）とノンユニークなロール（複数のアカウントに同時に付与可能）の両方をサポートしています。
ロールがユニークかノンユニークかはメタデータで指定できます。
コントラクトがメタデータを実装していない場合、すべてのロールはノンユニークとみなされます。

## カスタムデータ
dAppsは、ロールの割り当て時にロール固有の情報をエンコードするために`grantRole`関数の`_data`パラメーターを使用できます。
このカスタムデータは`roleData`関数を介して取得でき、`RoleGranted`イベントと共に送信されます。
開発者はこれをオンチェーンとオフチェーンのアプリケーションに統合できます。

## ロール承認
**ERC7432**は、ユーザーが他のアカウントにロールの付与と取り消しを承認する機能を提供します。
ユーザーは単一のNFTまたはコレクション内のすべてのNFTを管理できるアカウントを認可できます。
これにより、所有権を持たないコントラクトが**ERC7432**を操作できるようになります。

# 後方互換性
**ERC7432**は、トークンのアドレス（`tokenAddress`）とトークンのID（`tokenId`）をすべての関数とイベントで提供することを要求します。
これにより、NFTのロールに関する情報を一元的に管理できる独立した**ERC7432**コントラクトを使用できます。
これは、他のNFT関連の関数（例：`ownerOf`や`balanceOf`）が不要であることを意味し、さまざまなNFT実装との統合がスムーズに行えるようになります。

# 参考実装

以下のGithubに格納しています。

https://github.com/cardene777/base-code/blob/develop/contract/contracts/ERC7432/ERC7432.sol

---

### roleAssignments

```javascript
mapping(address => mapping(address => mapping(address => mapping(uint256 => mapping(bytes32 => RoleData))))) public roleAssignments;
```

**概要**

ロールの割り当て情報を格納するマッピング配列。
各キーに対応するロールの情報が保持されます。

**詳細**

- `grantor`
    - ロールを付与するアカウントのアドレス。
- `grantee`
    - ロールを受け取るアカウントのアドレス。
- `tokenAddress`
    - トークンのアドレス。
- `tokenId`
    - トークンのID。
- `role`
    - ロールを表すハッシュ。
- `RoleData`
    - ロールの有効期限とカスタムデータを格納する構造体。

---

### latestGrantees

```javascript
mapping(address => mapping(address => mapping(uint256 => mapping(bytes32 => address)))) public latestGrantees;
```

**概要**

最新の受益者（`grantee`）を追跡するマッピング配列。
各トークンとロールに対して最新の受益者が保持されます。

**詳細**

- `grantor`
    - ロールを付与するアカウントのアドレス。
- `tokenAddress`
    - トークンのアドレス。
- `tokenId`
    - トークンのID。
- `role`
    - ロールを表すハッシュ。
- `grantee`
    - 最新の受益者（`grantee`）のアドレス。

---

### tokenIdApprovals

```javascript
mapping(address => mapping(address => mapping(uint256 => mapping(address => bool)))) public tokenIdApprovals;
```

**概要**

トークンIDごとのオペレーターの承認情報を追跡するマッピング配列。

**詳細**

- `grantor`
    - ロールを付与するアカウントのアドレス。
- `operator`
    - オペレーターのアドレス。
- `tokenAddress`
    - トークンのアドレス。
- `isApproved`
    - オペレーターが承認されているかどうかの状態。

---

### tokenApprovals

```javascript
mapping(address => mapping(address => mapping(address => bool))) public tokenApprovals;
```

**概要**

トークンごとのオペレーターの承認情報を追跡するマッピング配列。

**詳細**

- `grantor`
    - ロールを付与するアカウントのアドレス。
- `operator`
    - オペレーターのアドレス。
- `tokenAddress`
    - トークンのアドレス。
- `isApproved`
    - オペレーターが承認されているかどうかの状態。

---

### validExpirationDate
```javascript
modifier validExpirationDate(uint64 _expirationDate) {
    require(_expirationDate > block.timestamp, "ERC7432: expiration date must be in the future");
    _;
}
```

**概要**
有効期限の日付が未来であることを確認する修飾子。

**詳細**

- `_expirationDate`
    - 確認される有効期限の日付。

---

### onlyApproved

```javascript
modifier onlyApproved(address _tokenAddress, uint256 _tokenId, address _grantor) {
    require(
        isRoleApprovedForAll(_tokenAddress, _grantor, msg.sender) ||
            getApprovedRole(_tokenAddress, _tokenId, _grantor, msg.sender),
        "ERC7432: sender must be approved"
    );
    _;
}
```

**概要**

指定された条件を満たす場合にのみ関数を実行できる修飾子。
送信者が承認されているかどうかを確認します。

**詳細**

- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンのID。
- `_grantor`
    - ロールを付与するアカウントのアドレス。

---

### grantRole

```javascript
function grantRole(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _grantee,
    uint64 _expirationDate,
    bytes calldata _data
) external {
    _grantRole(_role, _tokenAddress, _tokenId, msg.sender, _grantee, _expirationDate, _data);
}
```

**概要**
指定したロール（`_role`）を特定のトークン（`_tokenAddress`、`_tokenId`）の受託者（`_grantee`）に付与する関数。

**詳細**
権限の有効期限（`_expirationDate`）およびデータ（`_data`）も指定できます。

**引数**
- `_role`
    - 付与するロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantee`
    - ロールを受け取るアドレス。
- `_expirationDate`
    - ロールの有効期限（Unixタイムスタンプ形式）。
- `_data`
    - 任意のデータ。

---

### grantRoleFrom

```javascript
function grantRoleFrom(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _grantor,
    address _grantee,
    uint64 _expirationDate,
    bytes calldata _data
) external override onlyApproved(_tokenAddress, _tokenId, _grantor) {
    _grantRole(_role, _tokenAddress, _tokenId, _grantor, _grantee, _expirationDate, _data);
}
```

**概要**
特定のトークン（`_tokenAddress`、`_tokenId`）の所有者（`_grantor`）が指定したロール（`_role`）を受託者（`_grantee`）に付与する関数。

**詳細**
所有者の承認が必要です。

**引数**
- `_role`
    - 付与するロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantor`
    - ロールを付与する所有者のアドレス。
- `_grantee`
    - ロールを受け取るアドレス。
- `_expirationDate`
    - ロールの有効期限（Unixタイムスタンプ形式）。
- `_data`
    - 任意のデータ。

---

### _grantRole

```javascript
function _grantRole(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _grantor,
    address _grantee,
    uint64 _expirationDate,
    bytes calldata _data
) internal validExpirationDate(_expirationDate) {
    roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role] = RoleData(_expirationDate, _data);
    latestGrantees[_grantor][_tokenAddress][_tokenId][_role] = _grantee;
    emit RoleGranted( _role, _tokenAddress, _tokenId, _grantor, _grantee, _expirationDate, _data);
}
```

**概要**
ロールの付与と内部処理を行う関数。

**引数**
- `_role`
    - 付与するロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantor`
    - ロールを付与する所有者のアドレス。
- `_grantee`
    - ロールを受け取るアドレス。
- `_expirationDate`
    - ロールの有効期限（Unixタイムスタンプ形式）。
- `_data`
    - 任意のデータ。

---

### revokeRole

```javascript
function revokeRole(bytes32 _role, address _tokenAddress, uint256 _tokenId, address _grantee) external {
    _revokeRole(_role, _tokenAddress, _tokenId, msg.sender, _grantee);
}
```

**概要**
指定したロール（`_role`）を特定のトークン（`_tokenAddress`、`_tokenId`）の受託者（`_grantee`）から取り消す関数。

**詳細**
- `_role`
    - 取り消すロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantee`
    - ロールを持っている受託者のアドレス。

---

### revokeRoleFrom

```javascript
function revokeRoleFrom(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _revoker,
    address _grantee
) external override onlyApproved(_tokenAddress, _tokenId, _revoker) {
    _revokeRole(_role, _tokenAddress, _tokenId, _revoker, _grantee);
}
```

**概要**
特定のトークン（`_tokenAddress`、`_tokenId`）の所有者（`_revoker`）が指定したロール（`_role`）を受託者（`_grantee`）から取り消す関数。

**詳細**
所有者の承認が必要です。

**引数**
- `_role`
    - 取り消すロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_revoker`
    - ロールを取り消す所有者のアドレス。
- `_grantee`
    - ロールを持っている受託者のアドレス。

---

### _revokeRole

```javascript
function _revokeRole(
   

 bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _revoker,
    address _grantee
) internal {
    delete roleAssignments[_revoker][_grantee][_tokenAddress][_tokenId][_role];
    delete latestGrantees[_revoker][_tokenAddress][_tokenId][_role];
    emit RoleRevoked(_role, _tokenAddress, _tokenId, _revoker, _grantee);
}
```

**概要**
ロールの実際の取り消しと内部処理を行う関数。

**詳細**
- `_role`
    - 取り消すロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_revoker`
    - ロールを取り消す所有者のアドレス。
- `_grantee`
    - ロールを持っている受託者のアドレス。

---

### hasRole

```javascript
function hasRole(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _grantor,
    address _grantee
) external view returns (bool) {
    return roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role].expirationDate > block.timestamp;
}
```

**概要**
指定したトークン（`_tokenAddress`、`_tokenId`）の所有者（`_grantor`）が特定の受託者（`_grantee`）に対して役割（`_role`）を持っているか確認する関数。

**詳細**
- `_role`
    - 確認するロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantor`
    - ロールを持つ所有者のアドレス。
- `_grantee`
    - ロールを持っているか確認する受託者のアドレス。

**戻り値**
- `true`
    - 受託者がロールを持っており、有効期限内であることを示す`bool`値。
- `false`
    - 受託者が役割を持っていないか、有効期限が切れていることを示す`bool`値。

---

### hasUniqueRole

```javascript
function hasUniqueRole(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _grantor,
    address _grantee
) external view returns (bool) {
    return latestGrantees[_grantor][_tokenAddress][_tokenId][_role] == _grantee && roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role].expirationDate > block.timestamp;
}
```

**概要**
指定したトークン（`_tokenAddress`、`_tokenId`）の所有者（`_grantor`）が特定の受託者（`_grantee`）に対してロール（`_role`）を持っていて、そのロールがユニークか確認する関数。

**詳細**
- `_role`
    - 確認するロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantor`
    - ロールを持つ所有者のアドレス。
- `_grantee`
    - ロールを持っているか確認する受託者のアドレス。

**引数**
なし。

**戻り値**
- `true`
    - 受託者が唯一のロールを持っており、有効期限内であることを示す`bool`値。
- `false`
    - 受託者が唯一の役割を持っていないか、有効期限が切れていることを示す`bool`値。

---

### roleData

```javascript
function roleData(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _grantor,
    address _grantee
) external view returns (bytes memory data_) {
    RoleData memory _roleData = roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role];
    return (_roleData.data);
}
```

**概要**
指定したトークン（`_tokenAddress`、`_tokenId`）の所有者（`_grantor`）が特定の受託者（`_grantee`）に対してロール（`_role`）に関連付けられたデータを取得する関数。

**詳細**
- `_role`
    - データを取得するロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantor`
    - ロールを持つ所有者のアドレス。
- `_grantee`
    - データを取得する受託者のアドレス。

**戻り値**
- `data_`
    - ロールに関連付けられたデータ。

---

### roleExpirationDate

```javascript
function roleExpirationDate(
    bytes32 _role,
    address _tokenAddress,
    uint256 _tokenId,
    address _grantor,
    address _grantee
) external view returns (uint64 expirationDate_){
    RoleData memory _roleData = roleAssignments[_grantor][_grantee][_tokenAddress][_tokenId][_role];
    return (_roleData.expirationDate);
}
```

**概要**
指定したトークン（`_tokenAddress`、`_tokenId`）の所有者（`_grantor`）が特定の受託者（`_grantee`）に対してロール（`_role`）の有効期限を取得する関数。

**詳細**
- `_role`
    - 有効期限を取得するロールの識別子。
- `_tokenAddress`
    - トークンのアドレス。
- `_tokenId`
    - トークンの識別子。
- `_grantor`
    - ロールを持つ所有者のアドレス。
- `_grantee`
    - 有効期限を取得する受託者のアドレス。

**戻り値**
- `expirationDate_`
    - ロールの有効期限（Unixタイムスタンプ形式）。

---

### supportsInterface

```javascript
function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
    return interfaceId == type(IERC7432).interfaceId;
}
```

**概要**
指定したインターフェースがサポートされているかどうかを確認する関数。
特定のインターフェース（**IERC7432**）のサポートを確認するために使用されます。

**詳細**
特定のインターフェース（**IERC7432**）のサポートを確認するために使用されます。

**引数**
- `interfaceId`
    - 確認するインターフェースの識別子。

**戻り値**
- `true`
    - 指定したインターフェースがサポートされています。
- `false`
    - 指定したインターフェースがサポートされていません。

---

### setRoleApprovalForAll

```javascript
function setRoleApprovalForAll(
    address _tokenAddress,
    address _operator,
    bool _isApproved
) external override {
    tokenApprovals[msg.sender][_tokenAddress][_operator] = _isApproved;
    emit RoleApprovalForAll(_tokenAddress, _operator, _isApproved);
}
```

**概要**
所有者が指定したトークン（`_tokenAddress`）の特定のオペレーター（`_operator`）に対するロール承認を設定または解除する関数。

**引数**
- `_tokenAddress`
    - ロール承認を設定または解除するトークンのアドレス。
- `_operator`
    - ロール承認を設定または解除するオペレーターのアドレス。
- `_isApproved`
    - ロール承認を設定する場合は`true`、解除する場合は`false`。

---

### approveRole

```javascript
function approveRole(
    address _tokenAddress,
    uint256 _tokenId,
    address _operator,
    bool _approved
) external override {
    tokenIdApprovals[msg.sender][_tokenAddress][_tokenId][_operator] = _approved;
    emit RoleApproval(_tokenAddress, _tokenId, _operator, _approved);
}
```

**概要**
所有者が指定したトークン（`_tokenAddress`、`_tokenId`）に対して特定のオペレーター（`_operator`）に対するロール承認を設定または解除する関数。

**引数**
- `_tokenAddress`
    - ロール承認を設定または解除するトークンのアドレス。
- `_tokenId`
    - ロール承認を設定または解除するトークンの識別子。
- `_operator`
    - ロール承認を設定または解除するオペレーターのアドレス。
- `_approved`
    - ロール承認を設定する場合は`true`、解除する場合は`false`。

---

### isRoleApprovedForAll

```javascript
function isRoleApprovedForAll(
    address _tokenAddress,
    address _grantor,
    address _operator
) public view override returns (bool) {
    return tokenApprovals[_grantor][_tokenAddress][_operator];
}
```

**概要**
所有者が指定したトークン（`_tokenAddress`）の特定のオペレーター（`_operator`）に対するロール承認が設定されているか確認する関数。

**引数**
- `_tokenAddress`
    - ロール承認を確認するトークンのアドレス。
- `_grantor`
    - ロール承認を設定した所有者のアドレス。
- `_operator`
    - ロール承認を確認するオペレーターのアドレス。

**戻り値**
- `true`
    - ロール承認が設定されています。
- `false`
    - ロール承認が設定されていません。

---

### getApprovedRole

```javascript
function getApprovedRole(
    address _tokenAddress,
    uint256 _tokenId,
    address _grantor,
    address _operator
) public view override returns (bool) {
    return tokenIdApprovals[_grantor][_tokenAddress][_tokenId][_operator];
}
```

**概要**
所有者が指定したトークン（`_tokenAddress`、`_tokenId`）に対して特定のオペレーター（`_operator`）に対するロール承認が設定されているか確認する関数。

**引数**
- `_tokenAddress`
    - ロール承認を確認するトークンのアドレス。
- `_tokenId`
    - ロール承認を確認するトークンの識別子。
- `_grantor`
    - ロール承認を設定した所有者のアドレス。
- `_operator`
    - ロール承認を確認するオペレーターのアドレス。

**戻り値**
- `true`
    - ロール承認が設定されています。
- `false`
    - ロール承認が設定されていません。

# セキュリティ考慮事項

NFTロールインターフェースを統合する開発者にとっての重要事項は以下になります。

- **適切なアクセス制御の確保**
    - 不正なロールの割り当てや取り消しを防ぐために、適切なアクセス制御メカニズムを実装しましょう。
    - これにより、権限のない操作を防ぎ、セキュリティを向上させます。
- **セキュリティ対策の検討**
    - スマートコントラクトの実装中に潜在的な攻撃（例：再入攻撃）を考慮し、適切なセキュリティ対策を実施しましょう。
    - セキュリティの確保は非常に重要です。
- **NFTの所有権の確認**
    - **ERC7432**はNFTの所有権を自動的に確認しないため、dAppはNFTの所有者を明示的にクエリし、正確な所有者（`_grantor`）を`hasRole`関数に渡す責任があります。
    - これにより、ロールの割り当てが正確であることを保証できます。
- **ユニークまたはノンユニークなロールの確認**
    - ロールがユニーク（一度に1つのアカウントにしか割り当てられない）かノンユニーク（複数のアカウントに同時に割り当てられる）かを確認する責任はdAppにあります。
    - ユニークなロールの場合、他のアカウントに割り当てられていないことを確認するために、`hasUniqueRole`関数を使用する必要があります。

# 引用

Ernani São Thiago (@ernanirst), Daniel Lima (@karacurt), "ERC-7432: Non-Fungible Token Roles [DRAFT]," Ethereum Improvement Proposals, no. 7432, July 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7432.

# 最後に

今回は「NFTのさまざまなロールを管理するインターフェースを提案している**ERC7432**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58