はじめに

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

今回は、ERC721に使用権と担保権という2つロールを追加し、様々なユースケースに対応できる仕組みを提案しているERC2615についてまとめていきます!

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

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

概要

ERC2615は、ERC721を拡張し、賃貸(レンタル)および担保(モーゲージ)の機能を追加する提案です。 これにより、NFTが現実世界の不動産のような性質を持つことが可能になります。

ERC2615では、NFTに対して以下のような利用が可能になります。

  • バーチャルアイテム(ゲーム内資産、デジタルアートなど)
  • 現実世界のアイテム(住宅、自動車など)
  • 知的財産権
  • DAOのメンバーシップトークン

ERC2615では、新しいロール(役割)が導入されます。 例えば、所有者(Owner)とは別に、使用者(User)= 借り手(テナント)の権利が設けられます。 また、NFTを担保として利用し、抵当権(Lien)を設定できるようになります。

ERC2615はERC721と完全な後方互換性を持っており、既存のNFT開発者も容易にこの機能を統合できます。

特に重要なのは、アプリケーションの実行において実際の権利を持つのは「所有者」ではなく「使用者」であるという点です。 そのため、アプリケーション開発者はERC2615の仕様に対応した設計を行う必要があります。

動機

従来のERC721には「所有者」という1つのロールしか存在せず、賃貸や担保設定のような現実的な利用形態を実現するのが困難でした。

たとえば、賃貸では信頼のために保証金を必要とし、担保として利用するにはNFTの所有権を一時的に別のコントラクトにロックする必要がありました。

この処理はERC721標準の外部で個別に行う必要があり、開発者の負担となっていました。

ERC2615では、借用権(テナントの権利)および抵当権をNFTの標準機能として定義することで、これらの課題を解決します。 これにより、レンタルやモーゲージ機能をアプリケーションに簡単に統合できるようになります。

仕様

ERC2615は、既存のERC721(NFT)標準を拡張し、NFTに賃貸(レンタル)と担保(モーゲージ)の機能を付与することを目的とした規格です。 これにより、NFTを「貸す」「借りる」「担保にして借金する」など、現実の資産に近い取り扱いが可能になります。

この標準では、以下の3つのロールが導入されます。

  • Owner
    • NFTの所有者。
  • User
    • NFTを一時的に利用する権利を持つ者(テナント)。
  • Lien Holder
    • NFTに担保権を設定している者。

各ロールにはそれぞれ異なる権限が与えられています。 例えば、OwnerはUser権限の譲渡ができ、Lien Holderは最終的にNFTの所有権も移転させることが可能です。

ERC2615はERC721との後方互換性を持っており、既存のERC721用のツールも継続して使用できます。

レンタルとモーゲージの仕組み

レンタルの流れ

rental-sequential.jpg

  1. AliceがNFTに対して「使用権の設定を許可」します。
  2. AliceがNFTをレンタルコントラクトに登録します。
  3. Bobがレンタルを申し込み、初期レンタル料を支払います。
  4. Bobに「使用権」が移転されます。
  5. Bobまたは第三者が定期的にレンタル料を登録します。
  6. Aliceがレンタル料を引き出せます。
  7. 契約期間満了や不履行に応じて、「使用権」がリセットされます。

モーゲージの流れ

mortgage-sequential.jpg

  1. AliceがNFTに対して「担保権の設定を許可」します。
  2. Aliceがローンを申請します。
  3. Bobがローンに応じて資金を送ります。
  4. NFTに担保が設定されます。
  5. Aliceが資金を引き出します。
  6. 返済が行われます。
  7. 完了時には担保が解除され、返済されなければNFTの所有権が移転します。

インターフェース

js
event TransferUser(address indexed from, address indexed to, uint256 indexed itemId, address operator);
event ApprovalForUser(address indexed user, address indexed approved, uint256 itemId);
event TransferOwner(address indexed from, address indexed to, uint256 indexed itemId, address operator);
event ApprovalForOwner(address indexed owner, address indexed approved, uint256 itemId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event LienApproval(address indexed to, uint256 indexed itemId);
event TenantRightApproval(address indexed to, uint256 indexed itemId);
event LienSet(address indexed to, uint256 indexed itemId, bool status);
event TenantRightSet(address indexed to, uint256 indexed itemId,bool status);

function balanceOfOwner(address owner) public view returns (uint256);
function balanceOfUser(address user) public view returns (uint256);
function userOf(uint256 itemId) public view returns (address);
function ownerOf(uint256 itemId) public view returns (address);

function safeTransferOwner(address from, address to, uint256 itemId) public;
function safeTransferOwner(address from, address to, uint256 itemId, bytes memory data) public;
function safeTransferUser(address from, address to, uint256 itemId) public;
function safeTransferUser(address from, address to, uint256 itemId, bytes memory data) public;

function approveForOwner(address to, uint256 itemId) public;
function getApprovedForOwner(uint256 itemId) public view returns (address);
function approveForUser(address to, uint256 itemId) public;
function getApprovedForUser(uint256 itemId) public view returns (address);
function setApprovalForAll(address operator, bool approved) public;
function isApprovedForAll(address requester, address operator) public view returns (bool);

function approveLien(address to, uint256 itemId) public;
function getApprovedLien(uint256 itemId) public view returns (address);
function setLien(uint256 itemId) public;
function getCurrentLien(uint256 itemId) public view returns (address);
function revokeLien(uint256 itemId) public;

function approveTenantRight(address to, uint256 itemId) public;
function getApprovedTenantRight(uint256 itemId) public view returns (address);
function setTenantRight(uint256 itemId) public;
function getCurrentTenantRight(uint256 itemId) public view returns (address);
function revokeTenantRight(uint256 itemId) public;

イベント

TransferUser
javascript
event TransferUser(address indexed from, address indexed to, uint256 indexed itemId, address operator);

Userロールが別のアドレスに譲渡された時に発行されるイベント。 NFTの使用権(Userロール)が、他者に移転されたことを通知するために発行されます。

パラメータ

  • from
    • 使用権を移転した元のアドレス。
  • to
    • 新たに使用権を得たアドレス。
  • itemId
    • 対象のNFTのID。
  • operator
    • 操作を実行したアドレス。
ApprovalForUser
javascript
event ApprovalForUser(address indexed user, address indexed approved, uint256 itemId);

Userロールに対する使用許可が与えられた時に発行されるイベント。 特定のアドレスに対して、Userロールを操作する権限を与えた際に通知されます。

パラメータ

  • user
    • 現在の使用権保持者。
  • approved
    • 承認されたアドレス。
  • itemId
    • 対象のNFTのID。
TransferOwner
javascript
event TransferOwner(address indexed from, address indexed to, uint256 indexed itemId, address operator);

NFTの所有者(Ownerロール)が変更された時に発行されるイベント。 所有権の移転があったことを通知するイベントです。

パラメータ

  • from
    • 前の所有者。
  • to
    • 新しい所有者。
  • itemId
    • 対象のNFTのID。
  • operator
    • 操作を実行したアドレス。
ApprovalForOwner
javascript
event ApprovalForOwner(address indexed owner, address indexed approved, uint256 itemId);

NFTの所有権に対する操作を他者に承認した時に発行されるイベント。 Ownerロールに関して、操作可能なアドレスを指定したことを通知します。

パラメータ

  • owner
    • 所有者アドレス。
  • approved
    • 承認されたアドレス。
  • itemId
    • 対象のNFTのID。
ApprovalForAll
javascript
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

NFTの全ての操作について、特定アドレスへの承認・解除が行われた時に発行されるイベント。 一括操作権限の設定時に発行されます。

パラメータ

  • owner
    • NFTの所有者。
  • operator
    • 承認されたオペレーターアドレス。
  • approved
    • 承認の有無(true/false)。
LienApproval
javascript
event LienApproval(address indexed to, uint256 indexed itemId);

NFTに対して担保権を設定することを承認した時に発行されるイベント。 他者に対して担保(Lien)を設定できる権限を付与したことを通知するイベントです。

パラメータ

  • to
    • 担保権を設定できるよう承認されたアドレス。
  • itemId
    • 対象のNFTのID。
TenantRightApproval
javascript
event TenantRightApproval(address indexed to, uint256 indexed itemId);

NFTに対して使用権(テナント権)を設定することを承認した時に発行されるイベント。 使用権の設定を行えるように承認されたアドレスを通知するイベントです。

パラメータ

  • to
    • 使用権の設定が許可されたアドレス。
  • itemId
    • 対象のNFTのID。
LienSet
javascript
event LienSet(address indexed to, uint256 indexed itemId, bool status);

NFTに担保権が設定・解除された時に発行されるイベント。 担保権の設定(または解除)操作が行われたことを通知します。

パラメータ

  • to
    • 担保権が設定されたアドレス。
  • itemId
    • 対象のNFTのID。
  • status
    • 設定が有効かどうか(true = 設定、false = 解除)。
TenantRightSet
javascript
event TenantRightSet(address indexed to, uint256 indexed itemId, bool status);

NFTに使用権(テナント権)が設定・解除された時に発行されるイベント。 使用権の設定あるいは解除が行われたことを通知します。

パラメータ

  • to
    • 使用権が設定されたアドレス。
  • itemId
    • 対象のNFTのID。
  • status
    • 設定が有効かどうか(true = 設定、false = 解除)。

関数

balanceOfOwner
javascript
function balanceOfOwner(address owner) public view returns (uint256);

指定されたアドレスが所有するNFTの数を返す関数。 Ownerロールとして保持しているNFTの総数を取得することができます。

引数

  • owner
    • 対象の所有者アドレス。

戻り値

  • uint256
    • 所有しているNFTの個数。
balanceOfUser
javascript
function balanceOfUser(address user) public view returns (uint256);

指定されたアドレスが使用権を持っているNFTの数を返す関数。 Userロールとして借用しているNFTの総数を取得できます。

引数

  • user
    • 対象の使用者アドレス。

戻り値

  • uint256
    • 使用権を持っているNFTの個数。
userOf
javascript
function userOf(uint256 itemId) public view returns (address);

NFTの使用権を持っているアドレスを取得する関数。 特定のNFTについて、現在そのNFTのUserロールを持つアドレスを返します。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 現在のUserのアドレス。

ownerOf

javascript
function ownerOf(uint256 itemId) public view returns (address);

NFTの所有者アドレスを取得する関数。 標準のERC721と同様に、指定したNFTのOwnerロールを持つアドレスを返します。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 現在のOwnerのアドレス。
safeTransferOwner
javascript
function safeTransferOwner(address from, address to, uint256 itemId) public;
function safeTransferOwner(address from, address to, uint256 itemId, bytes memory data) public;

NFTの所有権(Ownerロール)を安全に移転する関数。 fromアドレスからtoアドレスへ、NFTのOwnerロールを移転します。 対象のNFTにTenant Right(使用権)が設定されていない場合は、Userロールも一緒に移転されます。

引数

  • from
    • 現在の所有者アドレス。
  • to
    • 新しい所有者アドレス。
  • itemId
    • 対象のNFTのID。
  • data(2つ目のオーバーロードで使用)
    • 任意の追加データ。
safeTransferUser
javascript
function safeTransferUser(address from, address to, uint256 itemId) public;
function safeTransferUser(address from, address to, uint256 itemId, bytes memory data) public;

NFTの使用権(Userロール)を安全に移転する関数。 Userロールだけを移転するための関数であり、Ownerロールは移転されません。 レンタルにおいて使用権の貸し出しや移動を可能にします。

引数

  • from
    • 現在のUserアドレス。
  • to
    • 新しいUserアドレス。
  • itemId
    • 対象のNFTのID。
  • data(2つ目のオーバーロードで使用)
    • 任意の追加データ。

approveForOwner

javascript
function approveForOwner(address to, uint256 itemId) public;

NFTのOwnerロールに関して、他者への操作許可を与える関数。 指定したアドレスに対して、対象NFTのOwnerロールを操作する権限を付与します。

引数

  • to
    • 許可を与えるアドレス。
  • itemId
    • 対象のNFTのID。
getApprovedForOwner
javascript
function getApprovedForOwner(uint256 itemId) public view returns (address);

指定したNFTに対してOwnerロールの操作が許可されているアドレスを取得する関数。 approveForOwnerで設定された承認情報を取得できます。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 承認されたアドレス。
approveForUser
javascript
function approveForUser(address to, uint256 itemId) public;

NFTのUserロールに関して、他者への操作許可を与える関数。 対象NFTの使用権(Userロール)について、指定のアドレスに操作権限を付与します。

引数

  • to
    • 許可を与えるアドレス。
  • itemId
    • 対象のNFTのID。
getApprovedForUser
javascript
function getApprovedForUser(uint256 itemId) public view returns (address);

NFTの使用権(Userロール)に関して承認されているアドレスを取得する関数。 approveForUserで設定された承認アドレスを確認できます。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 承認されているアドレス。
setApprovalForAll
javascript
function setApprovalForAll(address operator, bool approved) public;

全NFTに対して一括で操作許可を与える関数。 特定のオペレーターに対して、すべてのNFTを対象とした操作を一括で許可または解除します。

引数

  • operator
    • 承認するオペレーターのアドレス。
  • approved
    • 承認または解除(true/false)。
isApprovedForAll
javascript
function isApprovedForAll(address requester, address operator) public view returns (bool);

一括承認されているか確認する関数。 指定されたリクエスターが、オペレーターに一括操作を許可しているかを確認します。

引数

  • requester
    • 承認者のアドレス。
  • operator
    • オペレーターのアドレス。

戻り値

  • bool
    • 一括承認されていればtrue、されていなければfalse。
approveLien
javascript
function approveLien(address to, uint256 itemId) public;

NFTに対する担保設定権限を他者に与える関数。 指定したアドレスに、NFTに担保(Lien)を設定する許可を与えます。

引数

  • to
    • 担保設定を許可するアドレス。
  • itemId
    • 対象のNFTのID。
getApprovedLien
javascript
function getApprovedLien(uint256 itemId) public view returns (address);

NFTに担保権を設定する許可があるアドレスを取得する関数。 指定されたNFTについて、担保設定が承認されたアドレスを返します。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 担保設定が承認されたアドレス。
setLien
javascript
function setLien(uint256 itemId) public;

NFTに担保(Lien)を設定する関数。 担保設定が事前に承認された状態で、この関数により実際にNFTに担保が設定されます。

引数

  • itemId
    • 対象のNFTのID。

getCurrentLien

javascript
function getCurrentLien(uint256 itemId) public view returns (address);

NFTに現在設定されている担保権者を取得する関数。 指定されたNFTに対し、現在担保が設定されている場合、そのLien Holderのアドレスを返します。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 担保権を持っているアドレス。
revokeLien
javascript
function revokeLien(uint256 itemId) public;

NFTに設定された担保(Lien)を解除する関数。 契約条件を満たしたときなどに、NFTに設定されていた担保を解除するために使用します。

引数

  • itemId
    • 対象のNFTのID。
approveTenantRight
javascript
function approveTenantRight(address to, uint256 itemId) public;

NFTの使用権(テナント権)設定を他者に承認する関数。 指定されたアドレスに、NFTのUserロール(使用権)を設定する権限を付与します。

引数

  • to
    • 使用権設定を承認するアドレス。
  • itemId
    • 対象のNFTのID。
getApprovedTenantRight
javascript
function getApprovedTenantRight(uint256 itemId) public view returns (address);

NFTに対して使用権設定が承認されているアドレスを取得する関数。 事前にapproveTenantRightで承認されたアドレスを確認できます。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 使用権設定が承認されているアドレス。
setTenantRight
javascript
function setTenantRight(uint256 itemId) public;

NFTに使用権(テナント権)を設定する関数。 使用権の承認を受けたアドレスが、実際にNFTにUserロールを設定するために使用します。

引数

  • itemId
    • 対象のNFTのID。
getCurrentTenantRight
javascript
function getCurrentTenantRight(uint256 itemId) public view returns (address);

NFTに現在設定されている使用者(Userロールの保持者)を取得する関数。 対象NFTにUserロールが設定されている場合、そのアドレスを返します。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • address
    • 使用者アドレス。
revokeTenantRight
javascript
function revokeTenantRight(uint256 itemId) public;

NFTに設定された使用権(テナント権)を解除する関数。 契約終了や支払い不履行などにより、NFTの使用権を取り消す場合に使用されます。

引数

  • itemId
    • 対象のNFTのID。

ERC-2615 Receiver

onERCXReceived
javascript
function onERCXReceived(address operator, address from, uint256 itemId, uint256 layer, bytes memory data) public returns(bytes4);

ERCX対応コントラクトがNFTを受け取ったときに呼ばれる関数。 safeTransferOwnerやsafeTransferUserにより、NFTが他のコントラクトに送信された際に、そのコントラクトが適切に受け取れるかを確認するために使用されます。

引数

  • operator
    • 呼び出しを行ったアドレス。
  • from
    • NFTを送信したアドレス。
  • itemId
    • 対象のNFTのID。
  • layer
    • オーナーかユーザーかを識別するレイヤー(0=Owner、1=User)。
  • data
    • 任意のデータ。

戻り値

  • bytes4
    • 正常に受け取ったことを示すマジック値。

ERC-2615 Extensions

ERC721Enumerable

ERC721Enumerableと似ている拡張機能です。

js
function totalNumberOfItems() public view returns (uint256);
function itemOfOwnerByIndex(address owner, uint256 index, uint256 layer)public view returns (uint256 itemId);
function itemByIndex(uint256 index) public view returns (uint256);
totalNumberOfItems
javascript
function totalNumberOfItems() public view returns (uint256);

コントラクト内で存在するNFTの総数を返す関数。 発行済みのNFTアイテム全体の個数を返します。

戻り値

  • uint256
    • 発行済みNFTの総数。
itemOfOwnerByIndex
javascript
function itemOfOwnerByIndex(address owner, uint256 index, uint256 layer) public view returns (uint256 itemId);

所有者または使用者が保有するNFTをインデックスで取得する関数。 ERC721Enumerableのように、特定のアドレスが持っているNFTのうち、何番目かを指定して取得します。 layerによって、OwnerかUserかを切り替えます。

引数

  • owner
    • 対象のアドレス。
  • index
    • 所持NFTのインデックス。
  • layer
    • OwnerかUserかを示す(0=Owner、1=User)。

戻り値

  • itemId
    • NFTのID。
itemByIndex
javascript
function itemByIndex(uint256 index) public view returns (uint256);

インデックス指定でNFTのIDを取得する関数。 全体のNFTの中から、インデックス番号に対応するNFT IDを返します。

引数

  • index
    • グローバルなインデックス番号。

戻り値

  • uint256
    • NFTのID。

メタデータ

ERC721のメタデータ拡張と似ている拡張機能です。

js
function itemURI(uint256 itemId) public view returns (string memory);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
itemURI
javascript
function itemURI(uint256 itemId) public view returns (string memory);

指定したNFTのメタデータURIを取得する関数。 通常のERC721のtokenURIに相当します。 メタデータの場所を表すURIを返します。

引数

  • itemId
    • 対象のNFTのID。

戻り値

  • string
    • メタデータURI。
name
javascript
function name() external view returns (string memory);

このNFTコントラクトの名称を取得する関数。 OpenSeaやウォレットで表示されるコントラクト名に使用されます。

戻り値

  • string
    • コントラクトの名前。
symbol
javascript
function symbol() external view returns (string memory);

NFTのシンボル(略称)を取得する関数。 「ABC」や「ERCX」などの略称を返します。

戻り値

  • string
    • シンボル名。

補足

レンタルにおける保証金が不要

従来のERC721では、NFTを信頼性のある形で貸し出すには、借主による悪意ある行動を防ぐための保証金が必要でした。 これは、一度NFTの所有権を移転してしまうと元に戻せないため、貸し手のリスクを回避する手段でした。

しかし、ERC2615では、所有者とは別に使用者(テナント)というロールを導入することで、このような保証金の必要性を排除しています。 貸し手は所有権を保持したまま、使用権のみを貸し出すことが可能です。

モーゲージ時の所有権移転(エスクロー)が不要

NFTを担保としてローンを受ける際、従来はNFT自体をスマートコントラクトに一時的に預ける(エスクロー)必要がありました。 これは、借主が返済を怠った場合に備えるためです。

しかし、この方法ではNFTが実質的にロックされ、アプリケーション側での使用ができなくなります。 多くのNFTアプリは所有者にサービスを提供するため、エスクロー中のNFTは実用性を失ってしまいます。

ERC2615は、担保権(Lien)を設定しつつも、NFTを利用可能な状態に保つことができるため、NFTの実用性を損なわずにモーゲージが可能です。

容易な導入

従来のERC721では、レンタルや担保の機能を自前で実装する必要があり、開発負荷が非常に高いものでした。 ERC2615を採用すれば、標準化されたインターフェースとロール設計により、簡単に統合することができます。

金銭処理を含まない設計

ERC2615は、NFTの中で金銭のやり取りやレンタル料の徴収・支払いなどの処理を含まない構成になっています。 これは、オープンな標準として設計されており、特定のプラットフォームに依存するリスクを排除し、開発者が自由に構築できる柔軟性を提供します。

互換性

ERC2615は、既存のERC721との互換性を保つために拡張機能として設計されています。

具体的には、ERC721で使用されている関数(balanceOf、ownerOf、approve、safeTransferFromなど)に加え、新たに導入された関数もそれらに似た設計思想で構成されているため、既存の開発者やツールにとって馴染みやすく導入コストも小さい点が大きな特徴です。

セキュリティ

外部コントラクトの重要性とリスク

ERC2615は、NFTに対して担保権(Lien)や使用権(Tenant Right)を設定する機能を提供しますが、実際のビジネスロジック、例えばローン契約の成立やレンタルの支払い管理などは、外部のスマートコントラクトによって制御される設計になっています。

そのため、外部コントラクトにバグや脆弱性が存在する場合、ERC2615の仕様が想定した動作が崩れるリスクがあります。 例えば、担保を設定したまま解除できない状態になったり、不正に使用権が奪われたりするような状況です。

つまり、ERC2615自体の設計が堅牢であっても、外部コントラクトの安全性が全体の信頼性を左右することになります。 ERC2615を利用する際には、外部コントラクトも含めて十分な監査とセキュリティ検証が必要です。

引用

Kohshi Shiba kohshi.shiba@gmail.com, "ERC-2615: Non-Fungible Token with mortgage and rental functions [DRAFT]," Ethereum Improvement Proposals, no. 2615, April 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2615.

最後に

今回は「ERC721に使用権と担保権という2つロールを追加し、様々なユースケースに対応できる仕組みを提案しているERC2615」についてまとめてきました! いかがだったでしょうか?

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

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