はじめに

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

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

今回は、ERC721とERC20の機能を併せ持つ半代替性トークンと呼ばれているERC3525についてまとめていきます!

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

概要

この提案は、セミファンジブルトークン(semi-fungible tokens)の標準についてまとめたものです。 この文書で説明されているスマートコントラクトインターフェースのセットは、ERC721互換のトークン標準を定義しています。

ERC721については以下を参照してください。

この標準は、「ID」、「SLOT」、「VALUE」というトリプルスカラーモデルを導入し、トークンのセミファンジブルな構造を表現します。 また、セミファンジブルな性質を反映したトークンの新しいTransfer(送付)モデルとApprove(承認)モデルも導入しています。

トークンはERC721と同等のIDプロパティ(トークンID)を持ち、トークン自体を一意のエンティティとして識別するために使用されます。 これにより、トークンをアドレス間で転送したり、ERC721互換の方法で特定のアドレスをApprove(承認)したりできます。

トークンには、数量的な性質を表すvalueプロパティも含まれています。 このvalueプロパティの意味は、ERC20トークンのbalanceプロパティとかなり似ています。

ERC20のbalanceプロパティとは、特定のアドレスがERC20トークンをどれくらい所有しているかを保持しているものです。

ERC20については以下を参照してください。

各トークンにはslot属性があり、同じスロットを持つ2つのトークンの値はファンジブル(同等)として扱われるため、トークンのvalueプロパティにファンジビリティ(代替性)を付加します。

このEIPでは、セミファンジビリティに対応した新しいトークンTransfer(送付)モデルを導入しています。 これには、同じslotの2つのトークン間での値の転送や、トークンからアドレスへの値の転送が含まれます。

トリプルスカラーモデル.png

ID、VALUE、SLOTについては上の図のような関係性になっています。

  • ID
    • トークンを一位に識別するための値。
    • ERC721と互換性を持たせるために重要な機能。
  • VALUE
    • トークンの量を表す。
    • 量は数という意味合いだけでなく、ゲームにおいての「攻撃力」や「HP」として使用することもできる。
  • SLOT
    • トークンが所属するカテゴリーやグループを示す値。
    • 同じスロットのトークンは価値が同じと見なされ交換が可能。

動機

トークン化は、暗号通貨におけるデジタル資産の利用と管理において重要なトレンドの一つです。 従来、トークン化には、FT(代替性トークン: ERC20)とNFT(非代替性トークン: ERC721)の2つのアプローチがありました。 FTは資産としての価値が同等なため交換することができます。 一方、NFTは識別子(トークンID)に基づいてデジタル資産を区別することができます。

しかし、両者には重要な欠点があります。 例えば、ERC20はユーザーが個々のデータ構造やカスタマイズ可能なプロパティの組み合わせごとに、別々のERC20コントラクトを作成する必要があります。 これにより、膨大な数のERC20コントラクトが作成されることになります。 一方、ERC721トークンは数量的な特徴を持たないため、計算、流動性、および管理性に大きな制約があります。 例えば、ERC721を使用して債券、保険ポリシー、またはベスティングなどの金融商品を作成する場合、トークンを送付したりトークンの一部を分割して送付することは難しいです。

数量的なERC20の特徴と、識別的なERC721の属性を持つセミファンジブルトークンを作成することで、上記の問題を解決しようとしています。 ERC3525という新しいトークンスタンダードでは、トークンの値を部分的にTransfer(送付)するための新しいTransfer(送付)モデルが導入され、例えば債券の一部の価値を別のアドレスにTransfer`(送付)することが可能になるります。

Transfer.png

仕様

すべてのERC3525に準拠したコントラクトは、ERC3525、ERC721、ERC165の各インターフェースを実装する必要があります。

IERC3525

javascript
pragma solidity ^0.8.0;

interface IERC3525 /* is IERC165, IERC721 */ {
    event TransferValue(uint256 indexed _fromTokenId, uint256 indexed _toTokenId, uint256 _value);

    event ApprovalValue(uint256 indexed _tokenId, address indexed _operator, uint256 _value);

    event SlotChanged(uint256 indexed _tokenId, uint256 indexed _oldSlot, uint256 indexed _newSlot);

    function valueDecimals() external view returns (uint8);

    function balanceOf(uint256 _tokenId) external view returns (uint256);

    function slotOf(uint256 _tokenId) external view returns (uint256);

    function approve(
        uint256 _tokenId,
        address _operator,
        uint256 _value
    ) external payable;

    function allowance(uint256 _tokenId, address _operator) external view returns (uint256);

    function transferFrom(
        uint256 _fromTokenId,
        uint256 _toTokenId,
        uint256 _value
    ) external payable;


    function transferFrom(
        uint256 _fromTokenId,
        address _to,
        uint256 _value
    ) external payable returns (uint256);
}

TransferValue

トークンのValueが別いのトークンにTransfer(送付)されたときに発行されるイベント。 トークンが他のトークンのValueをTransfer(送付)する場合や、Valueが0のトークン間でTransfer(送付)される場合にも発行されます。

  • _fromTokenId
    • VALUEのTransfer(送付)元のトークンID。
  • _toTokenId
    • VALUEのTransfer(送付)先のトークンのID。
  • _value
    • Transfer(送付)されるVALUEの量。

ApprovalValue

トークンのTransfer(送付)が承認されたVALUEの設定、または変更されときに発行されるイベント。 トークンの所有者、承認されたオペレータが、特定のトークンのVALUEを管理するために呼び出すことができる関数によって発行されます。

  • _tokenId
    • 承認されるトークンID。
  • _operator
    • 承認されるオペレータのアドレス。
  • _value
    • _operatorが管理できる最大VALUE。

SlotChanged

トークンのSLOTが設定または変更されたときに発行されるイベント。 トークンのSLOT属性が変更されたときにこのイベントが発行されます。

  • _tokenId
    • SLOTが設定または変更されるトークンID。
  • _oldSlot
    • トークンの変更前のSLOT。
  • _newSlot
    • トークンの更新されたSLOT。

valueDecimals

トークンのVALUEを表現する際の小数点以下の桁数を返す関数。 例えば、小数点以下6桁の場合、1,000,000でVALUEを割ることでVALUEの値を取得できます。 ERC20トークンとの互換性を考慮して、この関数はvalueDecimals()という名前になっています。

balanceOf

特定のトークンのVALUEを取得する関数。

  • _tokenId
    • VALUEを取得する対象のトークンID。

slotOf

特定のトークンのSLOT(属性)を取得する関数。

  • _tokenId
    • SLOTを取得する対象のトークンID。

approve

特定のトークンに対して、指定されたアドレス(_operator)が最大でいくらのVALUEを管理できるかを許可する関数。

  • _tokenId
    • 承認するトークンID。
  • _operator
    • 承認するオペレータのアドレス。
  • _value
    • _operatorが管理できる最大VALUE。 トークンの所有者、承認されたオペレータが、特定のトークンのVALUEを管理するために呼び出され、ApprovalValueイベントが発行されます。

allowance

特定のトークンに対して、指定されたアドレス(_operator)が現在許可されているVALUEを取得する関数。

  • _tokenId
    • VALUEの許可を確認する対象のトークンID。
  • _operator
    • 許可を確認するアドレス。

transferFrom

特定のトークンから別のトークンにVALUEをTransfer(送付)する関数。 Transfer(送付)元とTransfer(送付)先のトークンは同じSLOTを持っている必要があります。

  • _fromTokenId
    • VALUEをTransfer(送付)する元のトークンID。
  • _toTokenId
    • VALUEをTransfer(送付)する先のトークンID。
  • _value
    • Transfer(送付)するVALUEの量。 呼び出し元は、トークンの所有者、承認されたオペレータのアドレスである必要があります。 また、トークンのVALUEの範囲内でTransfer(送付)を行う必要があります。 Transfer(送付)が成功した場合、TransferValueイベントが発行されます。

transferFrom

特定のトークンから指定されたアドレス(_to)にVALUEをTransfer(送付)する関数。 呼び出し元は、トークンのVALUEを受け取るアドレス(_to)がERC3525トークンを受け取ることができることを確認する必要があります。

  • _fromTokenId
    • VALUEのTransfer(送付)元のトークンID。
  • _to
    • VALUEを受け取るアドレス。
  • _value
    • Transfer(送付)するVALUEの量。 新しいERC3525トークンを作成するか、指定されたアドレス(_to)が既に同じSLOTのトークンを持っている場合は、そのトークンにVALUEをTransfer(送付)します。 Transfer(送付)が成功した場合、TransferおよびTransferValueイベントが発行されます。

IERC3525SlotEnumerable

SLOTの列挙拡張はオプション機能です。 これにより、コントラクトはSLOTの全リストを公開し値を取得できるようになります。

javascript
pragma solidity ^0.8.0;

interface IERC3525SlotEnumerable is IERC3525 /* , IERC721Enumerable */ {

    function slotCount() external view returns (uint256);

    function slotByIndex(uint256 _index) external view returns (uint256);

    function tokenSupplyInSlot(uint256 _slot) external view returns (uint256);

    function tokenInSlotByIndex(uint256 _slot, uint256 _index) external view returns (uint256);
}

slotCount

コントラクトに保存されているSLOTの総数を取得する関数。 戻り値はSLOTの総数。

slotByIndex

コントラクトに保存されている全てのSLOTの中から、指定されたインデックスに対応するSLOTを取得する関数。 _indexパラメーターに、取得したいSLOTのインデックスを指定します。 戻り値は、指定されたインデックスに対応するSLOTを表す整数です。

tokenSupplyInSlot

特定のSLOTに属するトークンの総数を取得する関数。

  • _slot
    • 取得したいSLOT。 戻り値は、指定されたSLOTに属するトークンの総数。

tokenInSlotByIndex

特定のSLOTに属するトークンの中から、指定されたインデックスに対応するトークンIDを取得する関数。

  • _slot
    • 取得したSLOT。
  • _index
    • 取得したいトークンのインデックス。 戻り値は、指定されたインデックスに対応するトークンID。

IERC3525SlotApprovable

SLOTレベルでの承認はオプション機能です。 これにより、SLOTの承認をサポートしたいコントラクトは、オペレーターが同じSLOTで自分のトークンを管理できるようになります。

javascript
pragma solidity ^0.8.0;

interface IERC3525SlotApprovable is IERC3525 {
    event ApprovalForSlot(address indexed _owner, uint256 indexed _slot, address indexed _operator, bool _approved);

    function setApprovalForSlot(
        address _owner,
        uint256 _slot,
        address _operator,
        bool _approved
    ) external payable;

    function isApprovedForSlot(
        address _owner,
        uint256 _slot,
        address _operator
    ) external view returns (bool);
}

ApprovalForSlot

特定のオペレータが、特定のアドレスが所有する全てのSLOT内のトークンを管理できるように承認されるか、または承認が取り消されるときに発行されるイベント。

  • _owner
    • トークン所有者のアドレス。
  • _slot
    • 承認されるSLOT。
  • _operator
    • 承認または取り消しを行われるオペレータのアドレス。
  • _approved
    • _operatorが承認されている場合はtrue、取り消されている場合はfalseを表す。

setApprovalForSlot

特定の所有者の特定のSLOT内の全てのトークンを管理する権限を、他アドレスに承認または取り消すための関数。 呼び出し元は、_ownerかsetApprovalForAllを通じてオペレータとして認可されている必要があります。 この関数を実行すると、ApprovalForSlotイベントが発行されます。

  • _owner
    • ERC3525トークンの所有者のアドレス。
  • _slot
    • 承認または取り消しを行うSLOT。
  • _operator
    • 承認または取り消すオペレータのアドレス。
  • _approved
    • _operatorを承認する場合はtrue、取り消す場合はfalseを指定。

isApprovedForSlot

特定のアドレスが特定のSLOT内の全てのトークンを管理できる権限があるか確認する関数。 戻り値は、_operatorのアドレスが指定されたSLOTの全てのトークンを管理する権限がある場合はtrue、それ以外の場合はfalseを返す。

  • _owner
    • ERC3525トークンの所有者アドレス。
  • _slot
    • 確認するSLOT。
  • _operator
    • 確認するオペレータのアドレス。

ERC3525トークンレシーバー

スマートコントラクトが他のアドレスから値を受け取ったときに通知を受けたい場合、IERC3525Receiverインターフェースのすべての関数を実装する必要があります。

javascript
 pragma solidity ^0.8.0;

interface IERC3525Receiver {
    function onERC3525Received(address _operator, uint256 _fromTokenId, uint256 _toTokenId, uint256 _value, bytes calldata _data) external returns (bytes4);

}

onERC3525Received

ERC3525トークンの値が受け取られたときに呼び出される関数。 ERC3525トークンを受け取るスマートコントラクトは、この関数を実装しているかどうかをチェックする必要があります。 もし受け取るコントラクトがこの関数を実装している場合、ERC3525トークンコントラクトは、値の送付(transferFrom(uint256,uint256,uint256,bytes))の後にこの関数を呼び出す必要があります。 この関数は、トランスファーが受け入れられた場合は0x009ce20b(bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)')))を返さなければなりません。 Transfer(送付)が拒否された場合は、0x009ce20b以外の値を返すか、リバートしなければなりません。

  • _operator
    • Transfer(送付)を実行したアドレス。
  • _fromTokenId
    • 値をTransfer(送付)するトークンID。
  • _toTokenId
    • 値のTransfer(送付)先のトークンID。
  • _value
    • Transfer(送付)される値。
  • _data
    • 指定されたフォーマットのない追加のデータ

トークンの操作

このEIPでは、ERC721互換のトークンTransfer(送付)関数に加えて、2つの新しいTransfer(送付)モデルが導入されています。 具体的には、IDからIDへの値のTransfer(送付)と、IDからアドレスへの値のTransfer(送付)です。

javascript
function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable;
	
function transferFrom(uint256 _fromTokenId, address _to, uint256 _value) external payable returns (uint256 toTokenId_);
  1. transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) 同じSLOT内の別のトークン(_fromTokenIdで指定されたトークン)から別のトークン(_toTokenIdで指定されたトークン)への値のTransfer(送付)を行う関数。 _valueの値が元のトークンの値から差し引かれ、宛先トークンの値に加算されます。

  2. transferFrom(uint256 _fromTokenId, address _to, uint256 _value) 1つのトークン(_fromTokenIdで指定されたトークン)からアドレス(_toで指定されたアドレス)への値のTransfer(送付)を許可します。 実際には、値はアドレスが所有するトークンにTransfer(送付)され、宛先トークンのIDが返される必要があります。

承認ルール

このEIPでは4つの種類の承認関数が提供されており、以下のそれぞれ異なるレベルの承認を示しています。

フルレベルの承認 setApprovalForAll関数。 ERC721と互換性があり、所有者が保有するすべてのトークン、およびその値を含む、すべてのトークンを管理できるようにするフルレベルの承認を許可します。

スロットレベルの承認 setApprovalForSlot関数。 スロットレベルの承認を示すものであり、指定されたSLOTを持つすべてのトークンとそれに紐づく値の承認を許可します。

トークンIDレベルの承認 所有者が所有する特定のトークンID、およびその値を管理できるようにする。

値レベルの承認 指定されたトークンのVALUEの指定量の承認を許可する。

どの承認関数も、所有者であるか、より高いレベルの権限を持っているアドレスから実行される必要があります。

transferFromルール

  1. transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) 1つのトークンから別のトークンへの値のTransfer(送付)を示すもので、以下のルールに従う必要がある。
  • msg.senderが_fromTokenIdの所有者、承認されたオペレータ、または_valueの量のトークンの承認を受けたオペレータである場合にのみ成功し、それ以外の場合はrevertする。
  • _fromTokenIdまたは_toTokenIdが0のトークンIDであるか、存在しない場合はrevertする。
  • _fromTokenIdと_toTokenIdのSLOTが一致しない場合はrevertします。
  • _valueが_fromTokenIdの値またはオペレータへの許可を超える場合はrevertする。
  • _toTokenIdの所有者がスマートコントラクトの場合は、onERC3525Received関数をチェックし、関数が存在する場合は値のTransfer(送付)後にこの関数を呼び出し、結果が0x009ce20bと等しくない場合はrevertする。
  • TransferValueイベントを発行する。
  1. transferFrom(uint256 _fromTokenId, address _to, uint256 _value) 1つのトークンIDからアドレスへの値のTransfer(送付)を示すもので、以下のルールに従う必要がある。
  • _toアドレスが所有するERC3525トークンを見つけるか、新しいERC3525トークンを同じSLOTの_fromTokenIdとして作成して、Transfer(送付)された値を受け取る必要がある。
  • msg.senderが_fromTokenIdの所有者、承認されたオペレータ、または少なくとも_valueの量のトークンの承認を受けたオペレータである場合にのみ成功し、それ以外の場合はrevertする。
  • _fromTokenIdが0のトークンIDであるか、存在しない場合はrevertする。
  • _toがゼロアドレスの場合はrevertする。
  • _valueが_fromTokenIdの値、またはオペレータへの許可を超える場合はrevertする。
  • _toアドレスがスマートコントラクトの場合は、onERC3525Received関数をチェックし、関数が存在する場合は値のTransfer(送付)後にこの関数を呼び出し、結果が0x009ce20bと等しくない場合はrevertする。
  • TransferおよびTransferValueイベントを発行する。

メタデータ

メタデータ拡張子

ERC3525メタデータ拡張機能は、ERC721メタデータ拡張機能と互換性があります。 このオプションのインターフェイスは、ERC165標準インターフェイス検出で識別できます。

javascript
pragma solidity ^0.8.0;

interface IERC3525Metadata is
    IERC3525 /* , IERC721Metadata */
{
    function contractURI() external view returns (string memory);
    function slotURI(uint256 _slot) external view returns (string memory);
}

contractURI

現在のERC3525コントラクトのURIを取得する関数。 戻り値は、JSON形式のURIを返します。

slotURI

指定されたSLOTのURIを取得する関数。 戻り値は、JSON形式のURIを返します。

ERC3525メタデータ URI JSON スキーマ

  • contractURIの戻り値。
json
{
  "title": "Contract Metadata",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Contract Name"
    },
    "description": {
      "type": "string",
      "description": "Describes the contract"
    },
    "image": {
      "type": "string",
      "description": "Optional. Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing what this contract represents."
    },
    "external_link": {
      "type": "string",
      "description": "Optional. A URI pointing to an external resource."
    },
    "valueDecimals": {
      "type": "integer",
      "description": "The number of decimal places that the balance should display - e.g. 18, means to divide the token value by 1000000000000000000 to get its user representation."
    }
  }
}
  • slotURIの戻り値。
json
{
  "title": "Slot Metadata",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Identifies the asset category to which this slot represents"
    },
    "description": {
      "type": "string",
      "description": "Describes the asset category to which this slot represents"
    },
    "image": {
      "type": "string",
      "description": "Optional. Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing the asset category to which this slot represents."
    },
    "properties": {
      "type": "array",
      "description": "Each item of `properties` SHOULD be organized in object format, including name, description, value, order (optional), display_type (optional), etc."
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of this property."
          },
          "description": {
            "type": "string",
            "description": "Describes this property."
          }
          "value": {
            "description": "The value of this property, which may be a string or a number."
          },
          "is_intrinsic": {
            "type": "boolean",
            "description": "According to the definition of `slot`, one of the best practice to generate the value of a slot is utilizing the `keccak256` algorithm to calculate the hash value of multi properties. In this scenario, the `properties` field should contain all the properties that are used to calculate the value of `slot`, and if a property is used in the calculation, is_intrinsic must be TRUE."
          },
          "order": {
            "type": "integer",
            "description": "Optional, related to the value of is_intrinsic. If is_intrinsic is TRUE, it must be the order of this property appeared in the calculation method of the slot."
          },
          "display_type": {
            "type": "string",
            "description": "Optional. Specifies in what form this property should be displayed."
          }
        }
      }
    }
  }
}
  • tokenURIの戻り値。
json
{
  "title": "Token Metadata",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Identifies the asset to which this token represents"
    },
    "description": {
      "type": "string",
      "description": "Describes the asset to which this token represents"
    },
    "image": {
      "type": "string",
      "description": "Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing the asset to which this token represents."
    },
    "balance": {
      "type": "integer",
      "description": "THe value held by this token."
    },
    "slot": {
      "type": "integer",
      "description": "The id of the slot that this token belongs to."
    },
    "properties": {
      "type": "object",
      "description": "Arbitrary properties. Values may be strings, numbers, objects or arrays. Optional, you can use the same schema as the properties section of ERC-3525 Metadata JSON Schema for slotURI(uint) if you need a better description attribute."
    }
  }
}

補足

メタデータの生成

このトークン標準は、コレクタブルやゲームアイテムよりも金融商品に適した準交換性のあるアセットを表現するように設計されています。 デジタルアセットの透明性と安全性を確保するために、すべての実装はメタデータをオフチェーンのサーバーURLではなく、コントラクトコードから直接生成することを強く推奨しています。

トークンからアドレスへの値のTransfer

トークンの「VALUE(値)」はトークン自体のプロパティであり、アドレスとは関連していないため、VALUEをアドレスに直接Transfer(送付)することは、そのアドレスが所有するトークンにVALUEをTransfer(送付)することを意味します。

実装の観点から、トークンからアドレスへのVALUEのTransfer(送付)は次のように行われる可能性があります。

  1. 受け取りアドレスに新しいトークンを作成する。
  2. ソーストークンから新しいトークンにVALUEをTransfer(送付)する。

これにより、この関数はIDからIDへのTransfer(送付)メソッドと完全に独立しているわけではなく、上記のプロセスをラップしたものと見なすことができます。

特別な場合として、宛先アドレスがソーストークンと同じSLOT値を持つ1つ以上のトークンを所有している場合、このメソッドは以下のように代替実装がされます。

  1. アドレスが所有するトークンのうち、ソーストークンと同じSLOTを見つける。
  2. 見つかったSLOTにトークンにTransfer(送付)する。

上記の両方の実装は、この標準に準拠して扱われるべきです。

IDからアドレスへのTransfer(送付)関数を維持する目的は、ほとんどのウォレットアプリとの互換性のためです。 ほとんどのトークン標準では、トークンのTransfer(送付)先はアドレスであるため、この構文のラッピングはトークンから任意のアドレスにVALURをTransfer(送付)する機能をウォレットアプリが簡単に実装できるようにします。

SafeTransferの代わりの通知/承認メカニズム

ERC721と一部の互換性のあるトークン標準では、「SafeTransfer」モデルが導入されていますが、このメカニズムは送信者に異なるTranferモード(Transfer / SafeTransferFrom)の選択を委ねるため、以下のような潜在的な問題を引き起こす可能性があります。

  • 送信者は通常、2つのTranferメソッド(Transfer / SafeTransferFrom)のどちらを選択すべきかわらない。
  • 送信者がsafeTransferFromメソッドを呼び出すと、受信者のコントラクトがコールバック関数を実装していない場合、そのコントラクトが問題なくトークンを受け取ってもTransfer処理が失敗する可能性があります。

このEIPは、柔軟性と簡単さのためにシンプルな「Check、Notify and Response」モデルを定義しています。

追加のsafeTransferFromメソッドは必要ありません。 すべての呼び出し元は1種類のTransferを呼び出すだけで済みます。 すべてのERC3525コントラクトは、受け取りコントラクトにonERC3525Receivedが存在するかどうかをチェックし、存在する場合はその関数を呼び出さなければなりません。 任意のスマートコントラクトは、値を受け取った後に通知される目的でonERC3525Received関数を実装できます。 この関数は、Transfer(送付)が受け入れられた場合は0x009ce20b(bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)')))を返し、Transfer(送付)が拒否された場合は他の値を返します。

この通知/承認メカニズムには特別なケースがあります。 ERC3525はアドレスから自身への値VALUEのTransfer(送付)を許可しているため、onERC3525Receivedを実装したスマートコントラクトがVALUEを自身にTransfer(送付)する場合、onERC3525Receivedも呼び出されます。 これにより、コントラクトは自身にTransfer(送付)する機能と他のアドレスからの値の受け取りとの間で異なる受け入れのルールを実装できます。

異なる承認モデル間の関係

ERC721とERC3525トークン標準の互換性を持たせるため、トークンの値を操作する柔軟性を考慮して、次のような承認レベルの関係を定義しました。

IDの承認により、承認されたオペレーターがこのIDから部分的なVALUEをTransfer(送付)できるようになります。 これにより、IDのVALUEの承認が簡素化されます。 ただし、全てのトークンの承認では、所有権を他のアドレスに与えないように注意する必要があります。 setApprovalForAllにより、任意のトークンから部分的なVALUEをTransfer(送付)する権限、および任意のトークンから第三者に対して部分的なVALUEのTransfer(送付)を承認する権限が得られます。 これにより、アドレスが所有するすべてのトークンのVALUEのTransfer(送付)と承認が簡素化されます。

後方互換性:

このEIPは、ERC721と後方互換性があります。

参考実装:

公式の実装は以下にあります。

上記の実装をもとに以下のディレクトリ内にコードをまとめました。

テスト

以下の公式のテストコードをほとんどそのまま持ってきたものを格納しています。

sh
$ cd contract
$ npm install
$ npx hardhat test test/ERC6147/ERC6147.test.ts

上記コマンドでテストの実行ができます。

セキュリティに関する考慮事項:

VALUEレベルの承認とSLOTレベルの承認(オプション)はERC721の承認モデルとは独立しています。 したがって、このEIPの実装はこの原則に従う必要があります。

このEIPはERC721互換であるため、標準的なERC721トークンを保持し操作することができるすべてのウォレットとスマートコントラクトは、非互換の標準実装によるERC3525トークンの資産損失のリスクはありません。

引用

Will Wang (@will42w), Mike Meng myan@solv.finance, Yi Cai (@YeeTsai) yee.tsai@gmail.com, Ryan Chow ryanchow@solv.finance, Zhongxin Wu (@Nerverwind), AlvisDu (@AlvisDu), "ERC-3525: Semi-Fungible Token," Ethereum Improvement Proposals, no. 3525, December 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3525.

考察

ERC3525は金融系に使用されることを考えられていると書かれていましたが、もっと色々な場面で使用できそうな規格です。 例えばコミュニティを作成し、SBTと呼ばれる譲渡不可能トークンにしたのち、コミュニティ内にサブコミュニティを作成してSLOTで所属を表しながら、各メンバーのコミュニティの貢献ポイントをVALUEとして持たせるなどできそうですよね。 前半でも書かれていたように、ERC3525はNFTの価値を分割することができます。 例えば、ゲーム内のキャラクターをNFTにして、所有しているNFTキャラクターを貸し出せるとします。 貸し出すキャラクターのパワーが1000ある場合、400と600のパワーを持つ2体に分割してそれぞれを貸し出すとかもできそうな気がします。 分割できんなかったNFTが、実質分割したかのように表現できるようになったので、その強みを活かして何か面白そうなことができそうですね。

最後に

今回は「ERC721とERC20の機能を併せ持つ半代替性トークンと呼ばれているERC3525」についてまとめてきました! いかがだったでしょうか?

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

採用強化中!

CryptoGamesでは一緒に働く仲間を大募集中です。

この記事で書いた自分の経験からもわかるように、裁量権を持って働くことができて一気に成長できる環境です。 「ブロックチェーンやWeb3、NFTに興味がある」、「スマートコントラクトの開発に携わりたい」など、少しでも興味を持っている方はまずはお話ししましょう!