[ERC5727] 様々な機能を備えたSBTの仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、様々な機能を付け加えたSBT（ソウルバウンドトークン・バッジ・アカウントバウンドトークンとも呼ばれる）のためのインターフェースを提案している規格である**ERC5727**についてまとめていきます！

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

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

:::note warn
**5727**は現在（2023年10月2日）では「**Draft**」段階です。
:::

# 概要

このインターフェースは、ソウルバウンドトークン（SBT）のために設計されました。

:::note info
SBTは個人の重要な情報を表すトークン。
アイデンティティ（誰がそのトークンの所有者か）、資格（所有者の特定のスキルや権限）、所属（組織やグループへの所属）、評判（所有者の信頼性や評価）などが含まれます。
:::

このインターフェースは、交換可能なトークンと交換不可能なトークンの異なる種類のトークンをの組み合わせを整理することができます。
また、SBTのライフサイクルを管理するトークンの作成、転送、更新、削除などの基本的なメソッド（操作方法）を提供しています。

さらに、このインターフェースは、DAO（分散型自治組織）のガバナンス、委任（他の人に操作権限を委任すること）、トークンの有効期限の設定、アカウントの復旧など、高度な機能の拡張機能も提供しています。
これにより、SBTシステムをさらに柔軟かつカスタマイズ可能にすることができます。

:::note info
このインターフェースは、個人のアイデンティティ情報を確実に管理し、それを利用して様々なシナリオに適用できる堅牢なフレームワークを提供しています。
個別のプロジェクトやアプリケーションに合わせてSBTを構築し、拡張するためのツールとして活用できます。
:::

# 動機

Web3のエコシステムでは、通常、自由に転送および交換可能なトークンが主流です。
これらのトークンは、所有者が他の人に転送したり、取引したりできるように設計されています。
しかし、転送できないトークンが必要な場面も多く存在します。
例えば、コミュニティのメンバーシップカードは、その所有権が他人に移転されないようにする必要があります。

Ethereumコミュニティでは、このような転送不可能なトークンを作成しようとする試みが既に多く見られます。
しかし、これらの試みはファンジブル（交換可能な）トークンと非ファンジブル（交換不可能な）トークンの両方をサポートし、重要なユースケースに対応する拡張機能を提供する柔軟性に欠けています。

このインターフェースは、転送不可能な所有権を表すために使用でき、以下のような一般的なユースケースに対応する機能を提供します。

- SBTの細かいライフサイクル管理（例：トークンの生成、取消、購読、有効期限）。
- コミュニティ投票および委任を通じたSBTの管理（例：DAOのガバナンス、オペレーター）。
- SBTの回収（例：アカウントの回復とキーの交換）。
- ファンジブルおよび非ファンジブルなSBT（例：メンバーシップカードと忠誠ポイント）。
- スロットを使用したSBTのグループ化（例：バウチャー、ポイント、バッジの組み合わせによる複雑なリワードスキーム）。
- クレーム可能なSBT（例：エアドロップ、ギブアウェイ、紹介）。

ソウルバウンドトークンの共通インターフェースは、Web3エコシステムを豊かにし、分散型社会の成長を促進するだけでなく、多くの異なるアプリケーションやプロジェクトにおいて、非転送性トークンの実装を簡素化し、効果的にサポートします。

# 仕様

トークンは、固有の識別子であるトークンID（256ビットの数字）によって識別されます。
また、トークンはその価値を表すこともあります。

一方、スロットは、個別のグループを識別するためのスロットID（256ビットの数字）によって特定されます。
スロットは、ファンジブル（交換可能な）トークンと非ファンジブル（交換不可能な）トークンを一緒にまとめるために使われます。
これにより、トークンは一部分だけが交換可能な「**セミファンジブル**」な状態になります。
ただし、各トークンは同時に1つのスロットにしか所属できません。

## コア

コアメソッドはSBTのライフサイクルを管理するために使用されます。
これらのメソッドは、すべての**セミファンジブル**SBT実装をサポートする必要があります。

```javascript
/**
 * @title ERC5727 Soulbound Token Interface
 * @dev The core interface of the ERC5727 standard.
 */
interface IERC5727 is IERC3525, IERC5192, IERC5484, IERC4906 {
    /**
     * @dev MUST emit when a token is revoked.
     * @param from The address of the owner
     * @param tokenId The token id
     */
    event Revoked(address indexed from, uint256 indexed tokenId);

    /**
     * @dev MUST emit when a token is verified.
     * @param by The address that initiated the verification
     * @param tokenId The token id
     * @param result The result of the verification
     */
    event Verified(address indexed by, uint256 indexed tokenId, bool result);

    /**
     * @notice Get the verifier of a token.
     * @dev MUST revert if the `tokenId` does not exist
     * @param tokenId the token for which to query the verifier
     * @return The address of the verifier of `tokenId`
     */
    function verifierOf(uint256 tokenId) external view returns (address);

    /**
     * @notice Get the issuer of a token.
     * @dev MUST revert if the `tokenId` does not exist
     * @param tokenId the token for which to query the issuer
     * @return The address of the issuer of `tokenId`
     */
    function issuerOf(uint256 tokenId) external view returns (address);

    /**
     * @notice Issue a token in a specified slot to an address.
     * @dev MUST revert if the `to` address is the zero address.
     *      MUST revert if the `verifier` address is the zero address.
     * @param to The address to issue the token to
     * @param tokenId The token id
     * @param slot The slot to issue the token in
     * @param burnAuth The burn authorization of the token
     * @param verifier The address of the verifier
     * @param data Additional data used to issue the token
     */
    function issue(
        address to,
        uint256 tokenId,
        uint256 slot,
        BurnAuth burnAuth,
        address verifier,
        bytes calldata data
    ) external payable;

    /**
     * @notice Issue credit to a token.
     * @dev MUST revert if the `tokenId` does not exist.
     * @param tokenId The token id
     * @param amount The amount of the credit
     * @param data The additional data used to issue the credit
     */
    function issue(
        uint256 tokenId,
        uint256 amount,
        bytes calldata data
    ) external payable;

    /**
     * @notice Revoke a token from an address.
     * @dev MUST revert if the `tokenId` does not exist.
     * @param tokenId The token id
     * @param data The additional data used to revoke the token
     */
    function revoke(uint256 tokenId, bytes calldata data) external payable;

    /**
     * @notice Revoke credit from a token.
     * @dev MUST revert if the `tokenId` does not exist.
     * @param tokenId The token id
     * @param amount The amount of the credit
     * @param data The additional data used to revoke the credit
     */
    function revoke(
        uint256 tokenId,
        uint256 amount,
        bytes calldata data
    ) external payable;

    /**
     * @notice Verify if a token is valid.
     * @dev MUST revert if the `tokenId` does not exist.
     * @param tokenId The token id
     * @param data The additional data used to verify the token
     * @return A boolean indicating whether the token is successfully verified
     */
    function verify(
        uint256 tokenId,
        bytes calldata data
    ) external returns (bool);
}
```

---

### `Revoked`

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

**概要**
トークンが取り消された時に発行されるイベント。

**詳細**
トークンの所有者がトークンを取り消した場合に発行されます。
取り消されたトークンの所有者のアドレスとトークンのIDが記録されます。

**パラメータ**
- `from`
    - トークンの所有者のアドレス。
- `tokenId`
    - 取り消されたトークンのID。

---

### `Verified`

```javascript
event Verified(address indexed by, uint256 indexed tokenId, bool result);
```

**概要**
トークンが検証された時に発行されるイベント。

**詳細**
トークンが検証された結果を記録します。
検証を実行したアドレス、トークンのID、および検証の結果（真偽値）が含まれます。

**パラメータ**
- `by`
    - 検証を実行したアドレス。
- `tokenId`
    - 検証されたトークンのID。
- `result`
    - 検証の結果。
    - 真(`true`)は成功、偽(`false`)は失敗を示します。

理解しました。指定された形式で、各関数について説明いたします。

---

### `verifierOf`

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

**概要**

指定されたトークンの検証者のアドレスを取得する関数。

**詳細**

トークンのID (`tokenId`) を受け取り、そのトークンの検証者のアドレスを返します。
ただし、指定されたトークンが存在しない場合、関数は`revert`（実行を中止）します。

**引数**

- `tokenId`
    - 検証者を取得したいトークンのID。

**戻り値**

- `address`
    - 検証者のアドレス。

---

### `issuerOf`

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

**概要**

指定されたトークンの発行者のアドレスを取得する関数。

**詳細**

トークンのID (`tokenId`) を受け取り、そのトークンの発行者のアドレスを返します。
ただし、指定されたトークンが存在しない場合、関数は`revert`（実行を中止）します。

**引数**

- `tokenId`
    - 発行者を取得したいトークンのID。

**戻り値**

- `address`
    - 発行者のアドレス。

---

### `issue`

```javascript
function issue(
    address to,
    uint256 tokenId,
    uint256 slot,
    BurnAuth burnAuth,
    address verifier,
    bytes calldata data
) external payable;
```

**概要**

指定されたアドレスにトークンを指定のスロットに発行する関数。

**詳細**

アドレス(`to`)に対して、指定されたトークンID(`tokenId`)を指定のスロット(`slot`)に発行します。
また、トークンのバーン権限(`burnAuth`)と検証者のアドレス(`verifier`)を指定し、トークンを発行するための追加データ(`data`)も提供できます。
ただし、`to`アドレスがゼロアドレスの場合、または`verifier`アドレスがゼロアドレスの場合、関数は`revert`します。

**引数**

- `to`
    - トークンを発行するアドレス。
- `tokenId`
    - トークンID。
- `slot`
    - トークンを発行するスロット。
- `burnAuth`
    - トークンのバーン権限。
- `verifier`
    - 検証者のアドレス。
- `data`
    - トークンを発行するための追加データ。

---

### `issue`

```javascript
function issue(
    uint256 tokenId,
    uint256 amount,
    bytes calldata data
) external payable;
```

**概要**

指定されたトークンにクレジットを発行する関数。

**詳細**

トークンID(`tokenId`)に対して指定された金額のクレジットを発行します。
ただし、指定されたトークンが存在しない場合、関数は`revert`します。

**引数**

- `tokenId`
    - トークンID。
- `amount`
    - クレジットの金額。
- `data`
    - クレジットを発行するための追加データ。

---

### `revoke`

```javascript
function revoke(uint256 tokenId, bytes calldata data) external payable;
```

**概要**

指定されたアドレスからトークンを取り消す関数。

**詳細**

トークンID(`tokenId`)を指定されたアドレスから取り消します。
ただし、指定されたトークンが存在しない場合、関数は`revert`します。

**引数**

- `tokenId`
    - トークンID。
- `data`
    - トークンを取り消すための追加データ。

理解しました。指定された形式で、新たに提供された関数について説明いたします。

---

### `revoke`

```javascript
function revoke(
    uint256 tokenId,
    uint256 amount,
    bytes calldata data
) external payable;
```

**概要**

指定されたトークンからクレジットを取り消す関数。

**詳細**

トークンID(`tokenId`)に対して指定された金額のクレジットを取り消します。
ただし、指定されたトークンが存在しない場合、関数は`revert`（実行を中止）します。

**引数**

- `tokenId`
    - トークンID。
- `amount`
    - クレジットの金額。
- `data`
    - クレジットを取り消すための追加データ。

---

### `verify`

```javascript
function verify(
    uint256 tokenId,
    bytes calldata data
) external returns (bool);
```

**概要**

指定されたトークンが有効か確認する関数。

**詳細**

トークンID(`tokenId`)を受け取り、追加のデータ(`data`)を用いてトークンが有効であるかどうかを確認します。
トークンが有効である場合、関数は真(`true`)を返し、無効である場合は偽(`false`)を返します。
ただし、指定されたトークンが存在しない場合、関数は`revert`（実行を中止）します。

**引数**

- `tokenId`
    - トークンID。
- `data`
    - トークンを確認するための追加データ。

## 拡張機能

以下のすべての拡張は**ERC5727**の実装にとってオプションです。
全てを実装したり、1つも実装しなかったり、いくつか選択して実装しても良いです。

### Enumerable

オーナーのトークンを列挙するメソッドを提供する拡張。
コアインターフェイスと一緒に実装するが推奨されます。

```javascript
/**
 * @title ERC5727 Soulbound Token Enumerable Interface
 * @dev This extension allows querying the tokens of a owner.
 */
interface IERC5727Enumerable is IERC3525SlotEnumerable, IERC5727 {
    /**
     * @notice Get the number of slots of a owner.
     * @param owner The owner whose number of slots is queried for
     * @return The number of slots of the `owner`
     */
    function slotCountOfOwner(address owner) external view returns (uint256);

    /**
     * @notice Get the slot with `index` of the `owner`.
     * @dev MUST revert if the `index` exceed the number of slots of the `owner`.
     * @param owner The owner whose slot is queried for.
     * @param index The index of the slot queried for
     * @return The slot is queried for
     */
    function slotOfOwnerByIndex(
        address owner,
        uint256 index
    ) external view returns (uint256);

    /**
     * @notice Get the balance of a owner in a slot.
     * @dev MUST revert if the slot does not exist.
     * @param owner The owner whose balance is queried for
     * @param slot The slot whose balance is queried for
     * @return The balance of the `owner` in the `slot`
     */
    function ownerBalanceInSlot(
        address owner,
        uint256 slot
    ) external view returns (uint256);
}
```

---

### `slotCountOfOwner`

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

**概要**

指定された所有者のスロットの数を取得する関数。

**詳細**

所有者のアドレス(`owner`)を受け取り、その所有者が持つスロットの数を返します。

**引数**

- `owner`
    - スロットの数を取得する所有者のアドレス。

**戻り値**

- `uint256`
    - `owner`のスロットの数。

---

### `slotOfOwnerByIndex`

```javascript
function slotOfOwnerByIndex(
    address owner,
    uint256 index
) external view returns (uint256);
```

**概要**

指定された所有者の指定したインデックスのスロットを取得する関数。

**詳細**

所有者のアドレス(`owner`)と取得したいスロットのインデックス(`index`)を受け取り、そのインデックスに対応するスロットを返します。
ただし、指定されたインデックスが所有者のスロットの数を超える場合、関数は`revert`（実行を中止）します。

**引数**

- `owner`
    - スロットを取得する所有者のアドレス。
- `index`
    - 取得するスロットのインデックス。

**戻り値**

- `uint256`
    - 取得されるスロット。

---

### `ownerBalanceInSlot`

```javascript
function ownerBalanceInSlot(
    address owner,
    uint256 slot
) external view returns (uint256);
```

**概要**

指定された所有者の指定されたスロット内の残高を取得する関数。

**詳細**

所有者のアドレス(`owner`)と残高を取得するスロットの番号(`slot`)を受け取り、その所有者の指定されたスロット内の残高を返します。
ただし、指定されたスロットが存在しない場合、関数は`revert`（実行を中止）します。

**引数**

- `owner`
    - 残高を取得する所有者のアドレス。
- `slot`
    - 残高を取得するスロット。

**戻り値**

- `uint256`
    - `slot`内の`owner`の残高。

---

### Metadata

トークン、スロット、およびコントラクト自体のメタデータを取得するメソッドを提供する拡張。
トークン、スロット、コントラクト (つまり SBT コレクション) の外観とプロパティを指定する必要がある場合は、実装するが推奨されます。

```javascript
/**
 * @title ERC5727 Soulbound Token Metadata Interface
 * @dev This extension allows querying the metadata of soulbound tokens.
 */
interface IERC5727Metadata is IERC3525Metadata, IERC5727 {

}
```

### ガバナンス

投票を通じてトークンの発行と取り消しの権限を管理するための方法を提供する拡張。
トークンの新規発行や取り消しの権限を、コミュニティや特定の投票者の決定に委ねたいときに役立ちます。

```javascript
/**
 * @title ERC5727 Soulbound Token Governance Interface
 * @dev This extension allows issuing of tokens by community voting.
 */
interface IERC5727Governance is IERC5727 {
    enum ApprovalStatus {
        Pending,
        Approved,
        Rejected,
        Removed
    }

    /**
     * @notice Emitted when a token issuance approval is changed.
     * @param approvalId The id of the approval
     * @param creator The creator of the approval, zero address if the approval is removed
     * @param status The status of the approval
     */
    event ApprovalUpdate(
        uint256 indexed approvalId,
        address indexed creator,
        ApprovalStatus status
    );

    /**
     * @notice Emitted when a voter approves an approval.
     * @param voter The voter who approves the approval
     * @param approvalId The id of the approval
     */
    event Approve(
        address indexed voter,
        uint256 indexed approvalId,
        bool approve
    );

    /**
     * @notice Create an approval of issuing a token.
     * @dev MUST revert if the caller is not a voter.
     *      MUST revert if the `to` address is the zero address.
     * @param to The owner which the token to mint to
     * @param tokenId The id of the token to mint
     * @param amount The amount of the token to mint
     * @param slot The slot of the token to mint
     * @param burnAuth The burn authorization of the token to mint
     * @param data The additional data used to mint the token
     */
    function requestApproval(
        address to,
        uint256 tokenId,
        uint256 amount,
        uint256 slot,
        BurnAuth burnAuth,
        address verifier,
        bytes calldata data
    ) external;

    /**
     * @notice Remove `approvalId` approval request.
     * @dev MUST revert if the caller is not the creator of the approval request.
     *      MUST revert if the approval request is already approved or rejected or non-existent.
     * @param approvalId The approval to remove
     */
    function removeApprovalRequest(uint256 approvalId) external;

    /**
     * @notice Approve `approvalId` approval request.
     * @dev MUST revert if the caller is not a voter.
     *     MUST revert if the approval request is already approved or rejected or non-existent.
     * @param approvalId The approval to approve
     * @param approve True if the approval is approved, false if the approval is rejected
     * @param data The additional data used to approve the approval (e.g. the signature, voting power)
     */
    function voteApproval(
        uint256 approvalId,
        bool approve,
        bytes calldata data
    ) external;

    /**
     * @notice Get the URI of the approval.
     * @dev MUST revert if the `approvalId` does not exist.
     * @param approvalId The approval whose URI is queried for
     * @return The URI of the approval
     */
    function approvalURI(
        uint256 approvalId
    ) external view returns (string memory);
}
```

---

### `ApprovalStatus`

```javascript
enum ApprovalStatus {
    Pending,
    Approved,
    Rejected,
    Removed
}
```

**概要**

承認の状態を表す列挙型。

**パラメータ**
- `Pending`
    - 承認待ち。
- `Approved`
    - 承認済み。
- `Rejected`
    - 拒否済み。
- `Removed`
    - 承認が削除されました。

---

### `ApprovalUpdate`

```javascript
event ApprovalUpdate(
    uint256 indexed approvalId,
    address indexed creator,
    ApprovalStatus status
);
```

**概要**

トークンの発行に関する承認が変更された場合に発行されるイベント。

**パラメータ**

- `approvalId`
    - 承認のID。
- `creator`
    - 承認を作成したアドレス。
    - 承認が削除された場合、ゼロアドレスです。
- `status`
    - 承認の状態。

---

### `Approve`

```javascript
event Approve(
    address indexed voter,
    uint256 indexed approvalId,
    bool approve
);
```

**概要**

投票者が承認を承認した場合に発行されるイベント。

**詳細**

- `voter`
    - 承認を承認した投票者のアドレス。
- `approvalId`
    - 承認のID。
- `approve`
    - 承認が行われたかどうかを示すブール値。

了解しました。指定された形式で、新たに提供された関数について説明いたします。

---

### `requestApproval`

```javascript
function requestApproval(
    address to,
    uint256 tokenId,
    uint256 amount,
    uint256 slot,
    BurnAuth burnAuth,
    address verifier,
    bytes calldata data
) external;
```

**概要**

トークンの発行に関する承認を作成する関数。
承認はトークンを発行するための要求を表します。

**詳細**

呼び出し元が投票者であることを確認し、指定されたパラメータに基づいてトークンの発行の承認を作成します。
承認は、発行先(`to`)、トークンのID(`tokenId`)、数量(`amount`)、スロット(`slot`)、バーン認証情報 (`burnAuth`)、検証者(`verifier`)、および追加データ(`data`)から構成されます。

**引数**

- `to`
    - トークンを発行する所有者のアドレス。
- `tokenId`
    - 発行するトークンのID。
- `amount`
    - 発行するトークンの数量。
- `slot`
    - 発行するトークンのスロット。
- `burnAuth`
    - トークンの発行に使用するバーン認証情報。
- `verifier`
    - トークンの発行に使用する検証者のアドレス。
- `data`
    - トークンの発行に使用する追加データ。

### `removeApprovalRequest`

```javascript
function removeApprovalRequest(uint256 approvalId) external;
```

**概要**

指定した`approvalId`の承認要求を削除する関数。

**詳細**

呼び出し元が承認要求の作成者であることを確認し、承認要求が既に承認または拒否されている場合、または存在しない場合に`revert`します。
この関数を使用することで、不要な承認要求を削除できます。

**引数**

- `approvalId`
    - 削除する承認要求のID。

### `voteApproval`

```javascript
function voteApproval(
    uint256 approvalId,
    bool approve,
    bytes calldata data
) external;
```

**概要**

指定した`approvalId`の承認要求を承認または拒否する関数。

**詳細**

呼び出し元が投票者であることを確認し、承認要求が既に承認または拒否されている場合、または存在しない場合に`revert`します。
また、`approve`パラメータによって、承認が承認されたか拒否されたかを指定できます。
追加データ(`data`)は、承認を行うための追加情報を提供するために使用されます。

**引数**

- `approvalId`
    - 承認を承認または拒否する承認要求のID。
- `approve`
    - 承認が承認された場合は`true`、拒否された場合は`false`。
- `data`
    - 承認を承認するための追加データ（例：署名、投票権）。

### `approvalURI`

```javascript
function approvalURI(uint256 approvalId) external view returns (string memory);
```

**概要**

指定した`approvalId`の承認のURIを取得する関数。

**詳細**

指定された `approvalId` が存在しない場合に`revert`し、存在する場合はその承認のURIを返します。
URIは、承認に関連する外部データ（たとえば、承認の詳細情報やドキュメンテーションへのリンク）への参照を提供します。

**引数**

- `approvalId`
    - URIを取得する承認のID。

### デリゲート

スロットのミントの権利をオペレータに(オペレータから)委任する(委任解除する)メソッドを提供する拡張。
特定のスロットでのトークンのミントをオペレータに代行させたい場合に役立ちます。

```javascript
/**
 * @title ERC5727 Soulbound Token Delegate Interface
 * @dev This extension allows delegation of issuing and revocation of tokens to an operator.
 */
interface IERC5727Delegate is IERC5727 {
    /**
     * @notice Emitted when a token issuance is delegated to an operator.
     * @param operator The owner to which the issuing right is delegated
     * @param slot The slot to issue the token in
     */
    event Delegate(address indexed operator, uint256 indexed slot);

    /**
     * @notice Emitted when a token issuance is revoked from an operator.
     * @param operator The owner to which the issuing right is delegated
     * @param slot The slot to issue the token in
     */
    event UnDelegate(address indexed operator, uint256 indexed slot);

    /**
     * @notice Delegate rights to `operator` for a slot.
     * @dev MUST revert if the caller does not have the right to delegate.
     *      MUST revert if the `operator` address is the zero address.
     *      MUST revert if the `slot` is not a valid slot.
     * @param operator The owner to which the issuing right is delegated
     * @param slot The slot to issue the token in
     */
    function delegate(address operator, uint256 slot) external;

    /**
     * @notice Revoke rights from `operator` for a slot.
     * @dev MUST revert if the caller does not have the right to delegate.
     *      MUST revert if the `operator` address is the zero address.
     *      MUST revert if the `slot` is not a valid slot.
     * @param operator The owner to which the issuing right is delegated
     * @param slot The slot to issue the token in
     */

    function undelegate(address operator, uint256 slot) external;

    /**
     * @notice Check if an operator has the permission to issue or revoke tokens in a slot.
     * @param operator The operator to check
     * @param slot The slot to check
     */
    function isOperatorFor(
        address operator,
        uint256 slot
    ) external view returns (bool);
}
```

---

### `Delegate`

```javascript
event Delegate(address indexed operator, uint256 indexed slot);
```

**概要**

トークンの発行権が特定のオペレーターに委任された時に発行されるイベント。

**パラメータ**

- `operator`
    - 発行権が委任された所有者のアドレス。
- `slot`
    - トークンを発行するスロット。

---

### `UnDelegate`

```javascript
event UnDelegate(address indexed operator, uint256 indexed slot);
```

**概要**

トークンの発行権が特定のオペレーターから取り消された時に発行されるイベント。

**パラメータ**

- `operator`
    - 発行権が取り消された所有者のアドレス。
- `slot`
    - トークンを発行するスロット。

---

了解しました。指定された形式で、新たに提供された関数について説明いたします。

### `delegate` 関数

```javascript
function delegate(address operator, uint256 slot) external;
```

**概要**

指定したスロットに対して特定のオペレーターに権限を委任する関数。

**詳細**

特定のオペレーターに対してトークンの発行権を委任する時に使用されます。
委任されたオペレーターは、指定されたスロット内でトークンを発行する権限を持つことになります。

**引数**

- `operator`
    - 権限を委任する所有者のアドレス。
- `slot`
    - トークンを発行するスロット。

### `undelegate`

```javascript
function undelegate(address operator, uint256 slot) external;
```

**概要**

指定したスロットから特定のオペレーターの権限を取り消します。

**詳細**

特定のオペレーターからトークンの発行権を取り消す時に使用されます。
これにより、委任されたオペレーターは指定されたスロット内でトークンを発行する権限を喪失します。

**引数**

- `operator`
    - 権限を取り消す所有者のアドレス。
- `slot`
    - トークンを発行するスロット。

---

了解しました。指定された形式で、新たに提供された関数について説明いたします。

### `delegate`

```javascript
function delegate(address operator, uint256 slot) external;
```

**概要**

指定したスロットに対して特定のオペレーターに権限を委任する関数。

**詳細**

この関数は、特定のオペレーターに対してトークンの発行権を委任する時に使用されます。
委任されたオペレーターは、指定されたスロット内でトークンを発行する権限を持つことになります。

**引数**

- `operator`
    - 権限を委任する所有者のアドレス。
- `slot`
    - トークンを発行するスロット。

---

### `undelegate`

```javascript
function undelegate(address operator, uint256 slot) external;
```

**概要**

指定したスロットから特定のオペレーターの権限を取り消す関数。

**詳細**

特定のオペレーターからトークンの発行権を取り消す時に使用されます。
これにより、委任されたオペレーターは指定されたスロット内でトークンを発行する権限を喪失します。

**引数**

- `operator`
    - 権限を取り消す所有者のアドレス。
- `slot`
    - トークンを発行するスロット。

---

### `isOperatorFor`

```javascript
function isOperatorFor(address operator, uint256 slot) external view returns (bool);
```

**概要**

指定したオペレーターが特定のスロットでトークンを発行または取り消す権限を持っているか確認する関数。

**詳細**

指定したオペレーターが指定したスロットでトークンを発行または取り消す権限を持っているかどうかを確認します。
オペレーターが権限を持っている場合は`true`、持っていない場合は`false`を返します。

**引数**

- `operator`
    - 確認するオペレーターのアドレス。
- `slot`
    - 確認するスロット。

---

### `isOperatorFor`

```javascript
function isOperatorFor(address operator, uint256 slot) external view returns (bool);
```

**概要**

指定したオペレーターが特定のスロットでトークンを発行または取り消す権限を持っているか確認する関数。

**詳細**

指定したオペレーターが指定したスロットでトークンを発行または取り消す権限を持っているか確認します。
オペレーターが権限を持っている場合は`true`、持っていない場合は`false`を返します。

**引数**

- `operator`
    - 確認するオペレーターのアドレス。
- `slot`
    - 確認するスロット。

### リカバリー

トークンを利用できない古いオーナーからトークンを回収するための方法を提供する拡張。
ユーザーが危険にさらされたり、古いウォレットからトークンを取り戻すことができるようにする時に使用が推奨されます。

この拡張機能では、**EIP712**と互換性のある署名スキームを使用しており、読みやすさと使いやすさを確保しています。

:::note info
トークン回収プロセスは、トークンの正当なオーナーであることを確認しやすくなっています。
:::

```javascript
/**
 * @title ERC5727 Soulbound Token Recovery Interface
 * @dev This extension allows recovering soulbound tokens from an address provided its signature.
 */
interface IERC5727Recovery is IERC5727 {
    /**
     * @notice Emitted when the tokens of `owner` are recovered.
     * @param from The owner whose tokens are recovered
     * @param to The new owner of the tokens
     */
    event Recovered(address indexed from, address indexed to);

    /**
     * @notice Recover the tokens of `owner` with `signature`.
     * @dev MUST revert if the signature is invalid.
     * @param owner The owner whose tokens are recovered
     * @param signature The signature signed by the `owner`
     */
    function recover(address owner, bytes memory signature) external;
}
```

---

### `Recovered`

```javascript
event Recovered(address indexed from, address indexed to);
```

**概要**

トークンが特定の所有者から回収された時に発行されるイベント。

**詳細**

- `from`
    - トークンが回収される所有者のアドレス。
- `to`
    - トークンの新しい所有者のアドレス。

---

### `recover`

```javascript
function recover(address owner, bytes memory signature) external;
```

**概要**

指定された所有者からトークンを回収する関数。

**詳細**

指定された所有者からトークンを回収するために使用されます。
所有者は有効な署名を提供する必要があり、署名が無効である場合、関数は`revert`します。

**引数**

- `owner`
    - トークンが回収される所有者のアドレス。
- `signature`
    - 所有者によって署名されたトークン回収の証明。

### Expirable

トークンの有効期限を管理するメソッドを提供する拡張。
一定期間経過後にトークンを失効/無効にしたい場合に役立ちます。

```javascript
/**
 * @title ERC5727 Soulbound Token Expirable Interface
 * @dev This extension allows soulbound tokens to be expirable and renewable.
 */
interface IERC5727Expirable is IERC5727, IERC5643 {
    /**
     * @notice Set the expiry date of a token.
     * @dev MUST revert if the `tokenId` token does not exist.
     *      MUST revert if the `date` is in the past.
     * @param tokenId The token whose expiry date is set
     * @param expiration The expire date to set
     * @param isRenewable Whether the token is renewable
     */
    function setExpiration(
        uint256 tokenId,
        uint64 expiration,
        bool isRenewable
    ) external;
}
```

---

### `setExpiration`

```javascript
function setExpiration(
    uint256 tokenId,
    uint64 expiration,
    bool isRenewable
) external;
```

**概要**

指定されたトークンの有効期限を設定する関数。

**詳細**
トークンの有効期限が設定され、必要に応じてトークンの更新が可能かどうかも指定できます。

**引数**

- `tokenId`
    - 有効期限を設定するトークンの識別子。
- `expiration`
    - 設定する有効期限の日時。
    - この日時が過去の場合、関数は`revert`します。
- `isRenewable`
    - トークンが更新可能かどうかを示すブール値。
    - 更新可能な場合、トークンの有効期限が過ぎても更新できるかどうかを制御します。

# 補足

## トークンの保管モデル
この規格では、セミファンジブルトークンの保管モデルを採用しています。
これは、ファンジブル（交換可能な）トークンと非ファンジブル（交換不可能な）トークンの両方をサポートするために設計されたモデルで、セミファンジブルトークンの標準に着想を得ています。

このモデルでは、各スロットは異なる種類のSBTを表現するために使用できるため、**ERC1155**で使用されているモデルよりもSBT（ソウルバウンドトークン）の表現に適していることがわかりました。
例えば、DAOは1つのSBTコレクション内でメンバーシップSBT、ロールバッジ、評価などを持つことができます。

さらに、**ERC1155**とは異なり、この規格のインターフェースは似たようなトークンを区別するのに役立ちます。
これは、異なるエンティティから得られた資格スコアが、単に価値だけでなく、効果、有効期間、起源なども異なるためです。
ただし、これらのトークンは依然として同じスロットを共有し、それらはすべて個人の信頼性やメンバーシップなどに寄与します。

## 回収メカニズム
SBTの損失を防ぐために、トークンの所有者が自分のアドレスで署名した署名を提供することで、ユーザーがトークンを回収できる回収メカニズムを提案しています。
このメカニズムは、**ERC1271**という標準を参考にしています。

SBTはアドレスに結びついており、アドレスのアイデンティティを表現するために設計されているため、これは分割できないものと見なされます。
したがって、トークンの回収は、所有者のすべてのトークンを一括で移転するものとして考えるべきです。
このため、`transferFrom`や`safeTransferFrom`などではなく、`recover`関数を使用しています。

# 後方互換性

このEIPは、**[ERC721](https://eips.ethereum.org/EIPS/eip-721)**、**[ERC3525](https://eips.ethereum.org/EIPS/eip-3525)**、**[ERC4906](https://eips.ethereum.org/EIPS/eip-4906)**、**[ERC5192](https://eips.ethereum.org/EIPS/eip-5192)**、**[ERC5484](https://eips.ethereum.org/EIPS/eip-5484)**と互換性のある新しいトークンインタフェースを提案しています。。

また、このEIPは**[ERC165](https://eips.ethereum.org/EIPS/eip-165)**とも互換性があります。

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

https://chaldene.net/erc721

:::note info
**ERC3525**については以下を参考にしてください。
:::

https://qiita.com/cardene/items/9dc7bad8211af65fa285

:::note info
**ERC4906**については以下を参考にしてください。
:::

https://qiita.com/cardene/items/a1f334a059f08971cfaa

:::note info
**ERC5192**については以下を参考にしてください。
:::

https://qiita.com/cardene/items/01b2463cb87f391498a8

:::note info
**ERC5484**については以下を参考にしてください。
:::

https://qiita.com/cardene/items/12fbba291e17f96b4610

:::note info
**ERC165**については以下を参考にしてください。
:::

https://qiita.com/cardene/items/541184977a5cba51b759

# 参考実装

以下に実装コードを格納しています。

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

引用: https://eips.ethereum.org/EIPS/eip-5727

# セキュリティ考慮事項

このEIPでは、トークンの送付を行わないため、トークンの送付に関連するセキュリティの問題を含んでいません。

しかし、ユーザーは回収メカニズムを使用する時に慎重になる必要があります。
なぜなら、ユーザーが自分のプライベートキーを失った場合、自身のソウルバウンドトークンは潜在的な盗難の危険にさらされる可能性があるからです。
攻撃者は、ユーザーのアカウントで署名を作成し、被害者のすべてのSBTを回復できる可能性があります。
そのため、ユーザーは常にプライベートキーを安全に保管し、失わないようにする必要があります。
また、開発者には、SBTを回復するために複数の署名が必要な回復メカニズムを実装することが推奨されます。
これにより、より高いセキュリティを確保できます。

# 引用

Austin Zhu (@AustinZhu), Terry Chen <terry.chen@phaneroz.io>, "ERC-5727: Semi-Fungible Soulbound Token [DRAFT]," Ethereum Improvement Proposals, no. 5727, September 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5727.

# 最後に

今回は「様々な機能を付け加えたSBT（ソウルバウンドトークン・バッジ・アカウントバウンドトークンとも呼ばれる）のためのインターフェースを提案している規格である**ERC5727**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58