ERC998: Composable Non Funible Token
Disclaimer: まだ議論されているアレです
アドベントカレンダー向けにもう少しブラッシュアップします(12/14)
概要
例えばCryptoKittyだとKitty自体はERC721で表されるがKittyが食べたもの(chow)ものなどKittyに付随するものが色々と存在している。あるKitty A をtransferする場合はAに付随するTokenも一緒にTransferされるのが自然だよねというお話
ERC998ではあるERC721に紐づくERC721 / ERC20 tokenをまとめて扱えるような仕様が提案されている
目的:
This allows standard assets to be composed into complex compositions and traded using a single transfer.
トークンがトークンを所持するという構造をうまく抽象化して一般的に使えるスタンダードを作りたい
普通のERC20 / ERC721は特に意識をしなくてもComposeできるようにしたい
既存のStandardはそのまま使えるように
所感:
親(所有している側)のコントラクトアドレス取ったりする辺りがやたら複雑なので、もう少し筋の良い(シンプルな)提案がありえる気がしてる
child / parent / rootの用語が分かりづらい感はある
NOTE
「継承ではなくオブジェクトのcomposeを使ってプログラムを組もうぜ」という視点にインスパイアされているらしい。
ユースケース
Use Case — Legal
契約書で使われる標準的な文(Clause)をトークン化しておけば、契約書自体は標準的な文をComposeしたものとしてあらわすことができる。
Use Case — Supply Chain Provenance
Luxury Goods
明示的にWPに使うとは書いてないかも(議論している人の中にここの人がいる?)
Specification
提案されているのは下記、4つの仕様
Composeする側(top-down) と Composeされる側(bottom up)の両面の仕様が提案されている。
After several discussions with the community we settled on providing these 4 extensions to a standard ERC-721 NFT in order to support composable functionality:
Tokenの種類 (top-down / bottom-up)
top-down composable contract (Composeする側)
child token(所有しているトークン)を 保存(?)し追跡する
bottom-up composable contract (Composeされる側)
親のトークンを保存(?)し追跡する
提案されている4つの仕様
a. top-down : 通常のトークンをComposable なNFT / FTに所有させたときに使う
b. bottom-up: 何かしらのComposableなトークンを NFT / FTにtransferしたいときに使う
An ERC988ERC721(top-down composable)
他のERC721トークンを所有させることができるERC721token
An ERC998ERC20 (top-down composable)
ERC20 トークンを所有することのできるERC20 token
An ERC998ERC721 (bottom-up composable)
他のERC721に所有されることが(be owned)できるERC721 token
An ERC998ERC20 (bottom-up composable)
他のERC721に所有されることのできるERC721 token
If you want to transfer regular ERC721 tokens to non-fungible tokens, then use top-down composables.
> If you want to transfer non-fungible tokens to regular ERC721 tokens then use bottom-up composables.
NOTE:
ERC998の各トークンは、スマートコントラクトがどのインターフェイスを実装しているか検証するためのインターフェイスであるERC165に準拠しており、準拠しているかどうかを検証可能。
NOTE:
bottom-upとかtop-downとか何種類もなんで作るの?というお話
Two different kinds of composable (top-down and bottom-up) exist to handle different use cases. A regular ERC721 token cannot own a top-down composable, but it can own a bottom-up composable. A bottom-up composable cannot own a regular ERC721 but a top-down composable can own a regular ERC721 token. Having multiple kinds of composables enable different token ownership possibilities.
Composeするとは?
List構造や木構造を用いてERC721 or ERC20 tokenのコレクションをまとめて保持する
データ構造のrootにownerのaddressを持っており、ownerがコレクション全体を保持する
コレクション (the entire composition) はrootのownerのアドレスを変化せることで1回のトランザクションでtransferできる
ある1つのトークンがtop-downかつbottom-upであることもできる
A(ERC721) -> B(ERC721) -> C (ERC20) みたいな感じ
Ref:
With composable tokens it is possible to compose lists or trees of ERC721 and ERC20 tokens connected by ownership. Any such structure will have a single owner address at the root of the structure that is the owner of the entire composition. The entire composition can be transferred with one transaction by changing the root owner.
It is possible for a token to be one or more kinds of composable token.
ERC20系のComposable
ERC998ERC20TopDownComposableインターフェース
top-downなのえ
ERC20TopDownComposableはERC20の容器として振舞う
ERC20TopDownComposableはERC20のreceive hold transferを行うことができるERC721トークンである
ERC998ERC20 TopDownComposableにERC20を transferする方法は2つ
ERC223の transfer(address _to, uint256 _value, bytes _data);を用いて_to にTopdownComposableのアドレスをもちいる
ERC20側でTopdownComposableをapproveしておき getERC20(address _from, uint256 _tokenId, address _erc20Contract, uint256 _value) をTopdownComposable側から呼び出す
NOTE ERC223 :
transfer したとき _to がContractAddressだった場合は tokenFallBackを呼び出さなければならないというアレ
code: transfer.js
// ERC223のIssueに載ってるやつなのでちょっと記法が古いです
function transfer(address _to, uint _value, bytes _data, string _custom_fallback) public returns (bool success) {
if(isContract(_to)) {
if (balanceOf(msg.sender) < _value) revert();
balancesmsg.sender = safeSub(balanceOf(msg.sender), _value); balances_to = safeAdd(balanceOf(_to), _value); assert(_to.call.value(0)(bytes4(keccak256(_custom_fallback)), msg.sender, _value, _data));
emit Transfer(msg.sender, _to, _value, _data);
return true;
}
else {
return transferToAddress(_to, _value, _data);
}
}
Funtions
tokenFallback
code: tokenFallback.js
/// @notice A token receives ERC20 tokens
/// @param _from The prior owner of the ERC20 tokens
/// @param _value The number of ERC20 tokens received
/// @param _data Up to the first 32 bytes contains an integer which is the receiving tokenId.
function tokenFallback(address _from, uint256 _value, bytes _data) external;
balanceOfERC20
ERC998ERC20ComposableのERC20Token残高を確認する
code:balanceOfERC20.js
/// @notice Look up the balance of ERC20 tokens for a specific token and ERC20 contract
/// @param _tokenId The token that owns the ERC20 tokens
/// @param _erc20Contract The ERC20 contract
/// @return The number of ERC20 tokens owned by a token from an ERC20 contract
function balanceOfERC20(
uint256 _tokenId,
address _erc20Contract
)
external
view
returns(uint256);
transferERC20
子としてもっているERC20を別のアカウントか別のERC998ERC20Comosableにtransfer する
ERC998ERC20Composable -> ERC20
-> ERC20
code: transferERC20.js
/// @notice Transfer ERC20 tokens to address
/// @param _tokenId The token to transfer from
/// @param _value The address to send the ERC20 tokens to
/// @param _erc20Contract The ERC20 contract
/// @param _value The number of ERC20 tokens to transfer
function transferERC20(
uint256 _tokenId,
address _to,
address _erc20Contract,
uint256 _value
)
external;
transferERC223
This function must authenticate msg.sender.
送信前にmsg.sener をAuthenticateしなければならない
code: transferERC223.js
/// @notice Transfer ERC20 tokens to address or ERC20 top-down composable
/// @param _tokenId The token to transfer from
/// @param _value The address to send the ERC20 tokens to
/// @param _erc223Contract The ERC223 token contract
/// @param _value The number of ERC20 tokens to transfer
/// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to
function transferERC223(
uint256 _tokenId,
address _to,
address _erc223Contract,
uint256 _value,
bytes _data
)
external;
getERC20
ERC223インターフェースを満たしていないERC20トークンを ERC998ERC20 Composable -> _toへ転送するときに用いる
approveされたERC20トークンを受け取る
Before this function can be used the ERC20 top-down composable contract address must be approved in the ERC20 contract to transfer the ERC20 tokens.
this function must authenticate that msg.sender equals _from or has been approved in the ERC20 contract.
msg.sender == from と msg.sender is approvedを確認しなければならない
code: getERC20.js
/// @notice Get ERC20 tokens from ERC20 contract.
/// @param _from The current owner address of the ERC20 tokens that are being transferred.
/// @param _tokenId The token to transfer the ERC20 tokens to.
/// @param _erc20Contract The ERC20 token contract
/// @param _value The number of ERC20 tokens to transfer
function getERC20(
address _from,
uint256 _tokenId,
address _erc20Contract,
uint256 _value
)
external;
Events
Event ReceivedERC20
ERC20を受け取ったときにemitされるevent
code: ERC998ERC20TopDown.js
/// @dev This emits when a token receives ERC20 tokens.
/// @param _from The prior owner of the token.
/// @param _toTokenId The token that receives the ERC20 tokens.
/// @param _erc20Contract The ERC20 contract.
/// @param _value The number of ERC20 tokens received.
event ReceivedERC20(
address indexed _from,
uint256 indexed _toTokenId,
address indexed _erc20Contract,
uint256 _value
);
TransferERC20
ERC20を送信したときにemitされるイベント
code: ERC998ERC20.js
/// @dev This emits when a token transfers ERC20 tokens.
/// @param _tokenId The token that owned the ERC20 tokens.
/// @param _to The address that receives the ERC20 tokens.
/// @param _erc20Contract The ERC20 contract.
/// @param _value The number of ERC20 tokens transferred.
event TransferERC20(
uint256 indexed _fromTokenId,
address indexed _to,
address indexed _erc20Contract,
uint256 _value
);
/// @notice Get ERC20 tokens from ERC20 contract.
/// @param _from The current owner address of the ERC20 tokens that are being transferred.
/// @param _tokenId The token to transfer the ERC20 tokens to.
/// @param _erc20Contract The ERC20 token contract
/// @param _value The number of ERC20 tokens to transfer
function getERC20(
address _from,
uint256 _tokenId,
address _erc20Contract,
uint256 _value
)
external;
}
ERC20 Bottom-Up Composableインターフェース
ERC721にattachすることのできるERC20トークン
ERC721に所有されたとき(attachされた時)は token所有者(コントラクト)のaddresと親トークンのIDを持つ
ERC20 / ERC223に加えて幾つかのメソッドを持つ
親トークンのbalance参照
親トークン間(TopDownComposable間)でのtransferを可能にする
例えば、下記のmappingを追加することで実現するらしい
code: tracking.js
/// @dev This mapping tracks standard ERC20/ERC223 ownership, where an address owns
/// a particular amount of tokens.
mapping(address => uint) userBalances;
/// @dev This additional mapping tracks ERC998 ownership, where an ERC721 token owns
/// a particular amount of tokens. This tracks contractAddres => tokenId => balance
mapping(address => mapping(uint => uint)) nftBalances;
contractAddress => tokenID => balance)
the owning address of a token and the parent tokenId. ERC20 bottom-up composables add several methods to the ERC20 and ERC223 interfaces
Functions
balanceOfToken
親ERC721の保持しているトークン残高を見る
but checks for ownership by ERC721 tokens rather than user addresses.
code: balanceOfToken.js
/// @notice Get the balance of a non-fungible parent token
/// @param _tokenContract The contract tracking the parent token
/// @param _tokenId The ID of the parent token
/// @return amount The balance of the token
function balanceOfToken(
address _tokenContract, // 親の残高をtracingしているComposableのアドレス?
uint256 _tokenId
)
external
view
returns (uint256 amount);
transferToParent
ユーザアドレスからERC721のアドレスへtransferする関数
受け取り手がERC721かERC165のsupportInterface関数でチェックする
受け取り手のtokenが存在するかをownerOfを呼ぶことでチェックする (throw / ゼロアドレスはだめ)
TransferToParentをemitしなければいけない
transferするのに十分な残高を持っていなければthrowしなければいけない
code: transferToParent.js
/// @notice Transfer tokens from owner address to a token
/// @param _from The owner address
/// @param _toContract The ERC721 contract of the receiving token
/// @param _toToken The receiving token
/// @param _amount The amount of tokens to transfer
function transferToParent(
address _from,
address _toContract,
uint256 _toTokenId,
uint256 _amount
)
external;
transferFromParent
ERC721(コントラクトアドレス) -> 他のアドレスへ ERC721に所属するtokenをtransferする関数
TransferFromParentをemitしなければならない (通常のERC20 Transfer eventに加えて)
ERC721のコントラクトアドレスが保持している残高をチェックしなければならない
msg.senderがERC721をownしているかチェックし、していなければthrowしなければならない
code: transferFromParent.js
/// @notice Transfer token from a token to an address
/// @param _fromContract The address of the owning contract
/// @param _fromTokenId The owning token
/// @param _to The address the token is transferred to
/// @param _amount The amount of tokens to transfer
function transferFromParent(
address _fromContract,
uint256 _fromTokenId,
address _to,
uint256 _amount
)
external;
transferFromParentERC223
ERC223を転送する場合のtransfer関数
ERC721側のtokenFallbackを呼ばなければいけない
code: transferFromParentERC223.js
/// @notice Transfer token from a token to an address, using ERC223 semantics
/// @param _fromContract The address of the owning contract
/// @param _fromTokenId The owning token
/// @param _to The address the token is transferred to
/// @param _amount The amount of tokens to transfer
/// @param _data Additional data with no specified format, can be used to specify the sender tokenId
function transferFromParentERC223(
address _fromContract,
uint256 _fromTokenId,
address _to,
uint256 _amount,
bytes _data
)
external;
transferAsChild
ERC721 -> ERC721を行う関数
ERC721の子としてtransfer
TransferFromParentとTransferToParentを両方emitしなければならない
残高不足の場合はthrowしなければならない
msg.senderはfromのownerかどうか検証して、違う場合はthrowしなければならない
receipientがERC721を満たしているかを検証しなければならない
ownerOfを呼ぶことでreceipientのERC721が実際に存在することを確認しなければならない
code: transferAsChild.js
/// @notice Transfer a token from a token to another token
/// @param _fromContract The address of the owning contract
/// @param _fromTokenId The owning token
/// @param _toContract The ERC721 contract of the receiving token
/// @param _toToken The receiving token
/// @param _amount The amount tokens to transfer
function transferAsChild(
address _fromContract,
uint256 _fromTokenId,
address _toContract,
uint256 _toTokenId,
uint256 _amount
)
external;
Events
TransferToParent
親コントラクト(ERC721に)トークンがtransferされたときに発火する
code:TransferToParent.js
/// @dev This emits when a token is transferred to an ERC721 token
/// @param _toContract The contract the token is transferred to
/// @param _toTokenId The token the token is transferred to
/// @param _amount The amount of tokens transferred
event TransferToParent(
address indexed _toContract,
uint256 indexed _toTokenId,
uint256 _amount
);
TransferFromParent
親コントラクト間でトークンがtransferされたときに発火する
code: TransferFromParent.js
/// @dev This emits when a token is transferred from an ERC721 token
/// @param _fromContract The contract the token is transferred from
/// @param _fromTokenId The token the token is transferred from
/// @param _amount The amount of tokens transferred
event TransferFromParent(
address indexed _fromContract,
uint256 indexed _fromTokenId,
uint256 _amount
);
ERC721系のComposable
ERC721 Top-DownComposableのインターフェース
ERC721TopdownComposableはERC721の入れ物
ERC721TopDownComposableはERC721を receive hold transfer できる
ERC721TopdownComposableをtransferする方法は下記2つ
safeTranferFromがあるかないかで分ける
CryptoKittyとかはない
その1. safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)
_to が TopdownComposable Contract addressの時
The bytes data argument holds the integer value of the top-down composable tokenId that the ERC721 token is transferred to.
例を見ると分かるはず
その2. ERC721の approve を先に読んでおいて topdownComposable側の getChildを呼ぶ
example
code: example.js
uint256 tokenId = 6;
bytes memory tokenIdBytes = new bytes(32);
assembly { mstore(add(tokenIdBytes, 32), tokenId) }
ERC721(contractAddress).safeTransferFrom(userAddress, composableAddress, 3, tokenIdBytes);
ComposableもERC721なので
どのComposableを利用するか
Composableの中でどのIDのNFTを取り扱うのか
転送するERC721のaddressとtokenIdをdataにうめて渡す
若干煩雑間あるよね :thinking_face:
Functions
rootOwnerOf
_tokenIDのrootOwnerが見つかるまでデータ構造をtraverseして、見つかったrootOwnerIDを返却する
返り値の先頭4bytesはERC998を示すマジックナンバー 0xcd740db5 後ろの20bytesがrootOwnerのaddress
ERC998のrootOwnerOfを実装しているかはワカラナイので、返り値から判別する為に入れている
code: magicnumberERC998.js
// 0xcd740db5は下記に等しい
this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^
this.tokenOwnerOf.selector ^ this.ownerOfChild.selector;
code: rootOwnerOf.js
/// @notice Get the root owner of tokenId.
/// @param _tokenId The token to query for a root owner address
/// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value.
function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner);
traverse自体は再帰的に行ってく
メソッド名が分かりづらすぎるので変えたほうがいい気がしている
葉側から根側へ再帰的に探索しているはずなのでrootOwner -> rootOwnerOfChildとか呼ぶので、
単純な例(1)
deck.rootOwnerOf(100) で ownerのアドレスが直接帰ってくる
https://gyazo.com/b4eb56593e5652ee20dcd68ad7f7c9fe
単純な例(2)
artCollection.rootOwnerOf(100) -> id=101のアドレスが返ってくる
_ownerOfChild(address(this), _childTokenId); = id=101のオーナーアドレスを取得する
関数名が分かりづらい..再帰っぽく書いて欲しい(childに名前が変わってしまうので)
https://gyazo.com/2b80b359a72225a2f040aa6e0c21475f
実装上の都合というよりは、ISSUE立てた段階で分かり辛い感がある
Logic for rootOwnerOf(uint256 _tokenId)
If the token is a bottom-up composable and has a parent token then call rootOwnerOf for the parent token.
If the call was successful then the returned address is the rootOwner.
Otherwise call rootOwnerOfChild for the parent token. // 親を取ってるのに子のrootOwnerを取る的な表記で??となる
If the call was successful then the returned address is the rootOwner.
Otherwise get the owner address of the token and that is the rootOwner.
Otherwise call rootOwnerOfChild for the token
この手の仕様が決まるまでにもう整備されそう(これ自体はEIPとしてリポジトリに含まれてしまっているが果たして?
単純な例(3)
https://gyazo.com/03f4c841013a9dcd9c14aa8724f0fe4e
collection.rootOwnerOf(100) -> ArtAuction.rootOwnerOf(200) -> 木の根にあるownerのアドレスを取得
code: sample.js
bool callSuccess;
bytes memory calldata;
// 0xed81cdda == rootOwnerOfChild(address,uint256)
// rootOwnerOfChildをABIエンコードして呼び出し
calldata = abi.encodeWithSelector(0xed81cdda, address(this), _childTokenId);
assembly {
// staticcall= assemblyにあるReadOnlyの命令を実行するOPCODE
// staticcall(g, a, in, insize, out, outsize)
// identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modifications
// call contract at address a with input mem[in..(in+insize))
// providing g gas and v wei and output area mem[out..(out+outsize)) returning 0 on error (eg. out of gas) and 1 on success
callSuccess := staticcall(gas, rootOwnerAddress, add(calldata, 0x20), mload(calldata), calldata, 0x20)
if callSuccess {
rootOwner := mload(calldata)
}
}
if(callSuccess == true && rootOwner >> 224 == ERC998_MAGIC_VALUE) {
// Case 2: Token owner is other top-down composable
return rootOwner;
}
このEIPのやりたいこととしては更にbottom-up tokenを下側につけていくことなので、より複雑なケースを考慮している
rootOwnerOfChild
与えられた子トークン(所持されているトークン)のrootOwnerを見つけるまでデータ構造をtraverseする
返り値の先頭4バイトは rootOwnerOfと同様に0xcd740db5
rootOwnerOfChildが自分自身ではないケースってあるのか?
TBD
code: rootOWnerOfChild.js
/// @notice Get the root owner of a child token.
/// @param _childContract The contract address of the child token.
/// @param _childTokenId The tokenId of the child.
/// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value.
function rootOwnerOfChild(
address _childContract,
uint256 _childTokenId
)
public
view
returns (bytes32 rootOwner);
ownerOfChild
与えられた子トークンの親トークンのアドレスとtokenIdを取得するのに用いる
アドレスの先頭は0xcd740db5
上記と同じ
code: ownerOfChild.js
/// @notice Get the parent tokenId of a child token.
/// @param _childContract The contract address of the child token.
/// @param _childTokenId The tokenId of the child.
/// @return parentTokenOwner The parent address of the parent token and ERC998 magic value
/// @return parentTokenId The parent tokenId of _tokenId
function ownerOfChild(
address _childContract,
uint256 _childTokenId
)
external
view
returns (
address parentTokenOwner,
uint256 parentTokenId
);
onERC721Received
ERC721で定義されている関数
ERC721のReceiverのInterface onERC721Received
safeTransferFromが呼ばれたときにERC721で呼ばれる関数
_dataに点操作先の親トークンのIDを与える
onERC721Receivedが呼ばれることで、top-down composableがERC721が自身にtransferされたことと殿tokenIdがparentTokenIdなのかを知る
返り値は 0x150b7a02
bytes4(keccak256(abi.encodePacked("onERC721Received(address,address,uint256,bytes)"))).
code: onERC721Received.js
/// @notice A token receives a child token
/// @param _operator The address that caused the transfer.
/// @param _from The prior owner of the child token.
/// @param _childTokenId The token that is being transferred to the parent.
/// @param _data Up to the first 32 bytes contains an integer which is the receiving parent tokenId.
function onERC721Received(
address _operator,
address _from,
uint256 _childTokenId,
bytes _data
)
external
returns(bytes4);
transferChild
msg.senderをAuthenticate (childのownerかどうか)
子トークンを別のaddressにtranser
下記を呼ぶ
code: inside.js
ERC721(_childContract).transferFrom(this, _to, _childTokenId);
code: transferChild.js
/// @notice Transfer child token from top-down composable to address.
/// @param _fromTokenId The owning token to transfer from.
/// @param _to The address that receives the child token
/// @param _childContract The ERC721 contract of the child token.
/// @param _childTokenId The tokenId of the token that is being transferred.
function transferChild(
uint256 _fromTokenId,
address _to,
address _childContract,
uint256 _childTokenId
)
external;
safeTransferChild
上記のsafeTransfer版
code:safeTransferChild.js
/// @notice Transfer child token from top-down composable to address.
/// @param _fromTokenId The owning token to transfer from.
/// @param _to The address that receives the child token
/// @param _childContract The ERC721 contract of the child token.
/// @param _childTokenId The tokenId of the token that is being transferred.
function safeTransferChild(
uint256 _fromTokenId,
address _to,
address _childContract,
uint256 _childTokenId
)
external;
相手方がtop-down Composableのときはこっちを呼ぶ
dataの所にtransferするtokenIdを埋める
dataの形式は特に決まっていない
code: safeTransferChild.js
/// @notice Transfer child token from top-down composable to address or other top-down composable.
/// @param _fromTokenId The owning token to transfer from.
/// @param _to The address that receives the child token
/// @param _childContract The ERC721 contract of the child token.
/// @param _childTokenId The tokenId of the token that is being transferred.
/// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to
function safeTransferChild(
uint256 _fromTokenId,
address _to,
address _childContract,
uint256 _childTokenId,
bytes _data
)
external;
transferChildToParent
msg.senderをAuthenticate
子トークンをtopodownComposableから 別のERC721にtransferする
topdown Composable -> 別のERC721 へのbottom-up composable tokenのtransferを1トランザクションで完了させるよう設計されている
子トークンがbottomUp Composable Tokenの場合だけ使用できる
code: transferChildToParent.js
/// @notice Transfer bottom-up composable child token from top-down composable to other ERC721 token.
/// @param _fromTokenId The owning token to transfer from.
/// @param _toContract The ERC721 contract of the receiving token
/// @param _toToken The receiving token
/// @param _childContract The bottom-up composable contract of the child token.
/// @param _childTokenId The token that is being transferred.
/// @param _data Additional data with no specified format
function transferChildToParent(
uint256 _fromTokenId,
address _toContract,
uint256 _toTokenId,
address _childContract,
uint256 _childTokenId,
bytes _data
)
external
内部で下記を呼ぶ
code: inside.js
ERC998ERC721BottomUp(_childContract).transferToParent(
address(this),
_toContract,
_toTokenId,
_childTokenId,
_data
);
getChild
safeTransferChildを実装していなERC721tokenをtop-down Composableに所有させるための関数
approveする場合はこっちを利用
approve -> getChildの流れ
transferの流れ
ERC721 token側でcomposabeのコントラクトアカウントへapprove or setApprovalForAll する
ERC721 tokenのownerはtop-down composableのgetChildを呼ぶ
getChildでmsg.sender をAuthenticate
code: getChild
/// @notice Get a child token from an ERC721 contract.
/// @param _from The address that owns the child token.
/// @param _tokenId The token that becomes the parent owner
/// @param _childContract The ERC721 contract of the child token
/// @param _childTokenId The tokenId of the child token
function getChild(
address _from,
uint256 _tokenId,
address _childContract,
uint256 _childTokenId
)
external;
Events
MISC : memo書き
Authentication関連
rootOwnerOf
code: rootOwner.js
/// @notice Get the root owner of tokenId.
/// @param _tokenId The token to query for a root owner address
/// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value.
function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner);
実装例: msg.senderとrootOwnerが一致するかを確かめる = Authentication
code: approve.js
function approve(address _approved, uint256 _tokenId) external {
address rootOwner = address(rootOwnerOf(_tokenId));
require(
rootOwner == msg.sender || // rootOwnerがsenderかチェック
isApprovedForAll(rootOwner,msg.sender) // ERC721で定められているI/F operator (brokers/wallets/auctioneersなどがownerからapproveされているか)
);
emit Approval(rootOwner, _approved, _tokenId);
}
isApprovedForAll (ERC721) operator = brokers/wallets/auctioneers など、ownerに承認されたアカウントを想定
code: isApprovedForAll.js
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if _operator is an approved operator for _owner, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
rootOwnerを探すロジック:
木構造 or リスト構造でtokenを持っているのでrootOwnerを探すためにはデータ構造をTraverseする必要がある
EIPに書いてあるやつ初見だとループしそうに見えるが..
sample implementationを見てみよう
実装上はOwnerの種別によって場合分けが必要
code: rootOwner.js
// Use Cases handled:
// Case 1: Token owner is this contract and no parent tokenId.
// Case 2: Token owner is this contract and token
// Case 3: Token owner is top-down composable
// Case 4: Token owner is an unknown contract
// Case 5: Token owner is a user
// Case 6: Token owner is a bottom-up composable
// Case 7: Token owner is ERC721 token owned by top-down token
// Case 8: Token owner is ERC721 token owned by unknown contract
// Case 9: Token owner is ERC721 token owned by user
function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner) {
サンプル実装
お試しサイト?
rootOwnerOfとか見るにtraverseするあたりの動きをイメージするために読むなど
References
おさらいとしてのERC721
元になっているMediumの記事
公式サイト (domain取ってる!)
CoinMonkにアップデート毎にポストがあり、議論の流れが分かる
日本語の簡単な解説
LoomやasobimoがPartnerになっているプロジェクト
公開されているものの中では使っていない
staticcall