[ERC5633] ERC1155をSBTにする仕組みを理解しよう！

# はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

今回は、**ERC1155**を`transfer`できないSBTにする仕組みを提案している**ERC5633**についてまとめていきます！

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

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

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

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

# 概要

**ERC5633**は、**ERC1155**を拡張する提案です。
特徴は、「**ソウルバウンド（Soulbound）**」かどうかの性質をトークンごとに設定できるようにする点です。
ソウルバウンドとは、一度発行されたトークンが他のアカウントに移動できない性質のことで、譲渡不可能なトークンです。
**ERC5633**では、1つのコントラクト内でソウルバウンドなNFTとそうでないNFTの両方を扱える仕組みを提案しています。

# 動機

ゲーム「World of Warcraft（WoW）」に登場する「ソウルバウンドアイテム」からインスピレーションを受けています。
WoWでは、キャラクターが装備した瞬間から他のプレイヤーに渡せなくなるアイテムが存在します。
例えば、「Caliseaのネックレス」というレアなアイテムはソウルバウンドで譲渡できませんが、普通の低レベルのネックレスは譲渡可能だったりします。

Ethereumのコミュニティでも、こうした「譲渡不可なNFT（＝ソウルバウンドNFT）」が注目されており、アイテムの種類ごとにその性質を定義できる標準が求められています。
**ERC5633**により、1つのNFTコントラクトで複数タイプのトークンを管理し、それぞれが「譲渡可能かどうか」を独立して持てるようになります。
例えばゲーム内の装備や称号などを1つの仕組みで管理しやすくなるという利点があります。

**ERC5633**では、**ERC165**というインターフェース検出の仕組みを活用します。
ウォレットやアプリケーション側が、あるトークンIDがソウルバウンドかどうかを事前に確認できるようになります。
さらに、もしトークンがソウルバウンドでなければ、通常通り `transfer` 関数を使って他人に送ることが可能です。
逆にソウルバウンドであれば送ろうとしてもブロックされます。

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

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

このように、開発者はコントラクトレベルでソウルバウンドかどうかの確認ロジックを組み込むことができ、ユーザーにとっても予期せぬトークン移動の失敗を避けられる仕組みになっています。

# 仕様

```js:IERC5633.sol
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;

interface IERC5633 {
  /**
   * @dev Emitted when a token type `id` is set or cancel to soulbound, according to `bounded`.
   */
  event Soulbound(uint256 indexed id, bool bounded);

  /**
   * @dev Returns true if a token type `id` is soulbound.
   */
  function isSoulbound(uint256 id) external view returns (bool);
}
```

あるNFTのIDが `isSoulbound(uint256 id)` を実行したときに `true` を返せば、そのトークンはソウルバウンド（譲渡不可）と見なされます。
つまり、そのIDを持つトークンは他のアカウントに送ることができなくなります。

この場合、そのトークンに対して**ERC1155**で定義されている「`transfer`」系の関数はすべて使えなくなります。
正確に言うと、使おうとするとエラーになります。
ただし、`mint`（発行）と `burn` だけは例外で引き続き使えます。

### インターフェースの定義（`IERC5633`）

この仕様で使われるインターフェース `IERC5633` は以下の2つのイベントと関数を実装する必要があります。

- `event Soulbound(uint256 id, bool bounded)`  

特定のトークンIDがソウルバウンドとして設定・解除されたときに発行されるイベントです。
`bounded` が `true` ならソウルバウンド、`false` なら解除されたことを意味します。

- `function isSoulbound(uint256 id) external view returns (bool)`  

トークンIDがソウルバウンドかどうかを確認する関数です。

### ERC165との連携

このインターフェース `IERC5633` を実装するコントラクトは、**ERC165**の `supportsInterface` 関数も必ず実装する必要があります。
そして、`interfaceID` に `0x911ec470` が渡されたときには、必ず `true` を返さなければいけません。

これは「このコントラクトは `IERC5633` に対応している」と外部から確認するための仕組みです。
ウォレットや他のコントラクトがそのコントラクトに対して `supportsInterface(0x911ec470)` を呼ぶことで、ソウルバウンド対応かどうかが判断できるようになっています。

# 補足

コントラクト内のトークンが全てデフォルトでソウルバウンドトークンの場合、実装中は`isSoulbound(uint256 id)`がデフォルトで`true`を返す必要があります。

# 参考実装

```js:ERC5633.sol
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "./IERC5633.sol";

/**
 * @dev Extension of ERC1155 that adds soulbound property per token id.
 *
 */
abstract contract ERC5633 is ERC1155, IERC5633 {
    mapping(uint256 => bool) private _soulbounds;
    
    /// @dev See {IERC165-supportsInterface}.
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155) returns (bool) {
        return interfaceId == type(IERC5633).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns true if a token type `id` is soulbound.
     */
    function isSoulbound(uint256 id) public view virtual returns (bool) {
        return _soulbounds[id];
    }

    function _setSoulbound(uint256 id, bool soulbound) internal {
        _soulbounds[id] = soulbound;
        emit Soulbound(id, soulbound);
    }

    /**
     * @dev See {ERC1155-_beforeTokenTransfer}.
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            if (isSoulbound(ids[i])) {
                require(
                    from == address(0) || to == address(0),
                    "ERC5633: Soulbound, Non-Transferable"
                );
            }
        }
    }
}
```

# 引用

HonorLabs (@honorworldio), "ERC-5633: Composable Soulbound NFT, EIP-1155 Extension [DRAFT]," Ethereum Improvement Proposals, no. 5633, September 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5633.

# 最後に

今回は「**ERC1155**を`transfer`できないSBTにする仕組みを提案している**ERC5633**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

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