ERC998是可拆解(Composable)的ERC721
父币指的是ERC-998 token, 子币指的是 父币所拥有的ERC-721 或者 ERC-20 tokens .
/// tokenId of composable, mapped to child contract address /// child contract address mapped to child tokenId or amount mapping(uint256 => mapping(address => uint256)) children;
ERC998合约的mapping是由父币的TokenID映射到一个子币的mapping:子币的合约地址到子币TokenID的映射。
如果子币是ERC721, mapping是子币的合约地址到子币TokenID的映射。
如果子币是ERC20, mapping是子币的合约地址到子币TokenID的数量
增加子币
/// add ERC-721 children by tokenId
/// @requires owner to approve transfer from this contract
/// call _childContract.approve(this, _childTokenId)
/// where this is the address of the parent token contract
addChild(
uint256 _tokenId,
address _childContract,
uint256 _childTokenId
) {
// call the transfer function of the child contract
// if approve was called with the address of this contract
// the ownership of the child token(s) will be transferred to this contract
require(
_childContract.call(
bytes4(sha3("transferFrom(address,address,uint256)")),
msg.sender, this, _childTokenId
)
);
// if successful, add children to the mapping
// generate a 'pseudo address' for the specific child tokenId
// address construction is analogous to 'contract address + nonce'
// use 0 == no child token, and 1 == child token exists
address childToken = address(
keccak256(_childContract, _childTokenId)
);
children[_tokenId][childToken] = 1;
}
/// add ERC-20 children by amount
/// @requires owner to approve transfer from this contract
/// call _childContract.approve(this, _amount)
addChildAmount(
uint256 _tokenId,
address _childContract,
uint256 _amount
) {
// call the transfer function of the child contract
// if approve was called with the address of this contract
// the ownership of the child token(s) will be transferred to this contract
require(
_childContract.call(
bytes4(sha3("transferFrom(address,address,uint256)")),
msg.sender, this, _amount
)
);
// if successful, add children to the mapping
children[_tokenId][_childContract] += _amount;
}
条件:
- 函数调用的发送者拥有父币, the ERC-721规范定义并实现了一个标准方法
_owns(address, tokenId)
- 通过子币合约地址,将父币token ID映射到子币token ID, 强制通过Bookkeeping映射保持父子关系
转账NFT - ERC721子币
/// transfer ERC-721 child by _childTokenId transferChild( address _to, uint256 _tokenId, address _childContract, uint256 _childTokenId ) { // require ownership of parent token && // check parent token owns the child token // use the 'pseudo address' for the specific child tokenId address childToken = address( keccak256(_childContract, _childTokenId) ); require(_owns(msg.sender, _tokenId)); require(children[_tokenId][childToken] == 1); require( _childContract.call( // if true, transfer the child token bytes4(sha3("transfer(address,uint256)")), _to, _childTokenId ) ); // remove the parent token's ownership of the child token children[_tokenId][childToken] = 0; }
条件:
- 函数调用的发送者拥有父币
- 父币有足够的ERC20的账户余额
转账FT - ERC20子币
/// transfer ERC-20 child by _amount transferChildAmount( address _to, uint256 _tokenId, address _childContract, uint256 _amount ) { // require ownership of parent token && // check parent token owns enough balance of the child tokens require(_owns(msg.sender, _tokenId)); require(children[_tokenId][_childContract] >= _amount); require( _childContract.call( // if true, transfer the child tokens // not a delegate call, the child tokens are owned by this contract bytes4(sha3("transfer(address,uint256)")), _to, _amount ) ); //decrement the parent token's balance of child tokens by _amount children[_tokenId][_childContract] -= _amount; }
条件:
- 函数调用的发送者拥有父币
- 父币有足够的ERC20的账户余额
ERC-998 Composable Non-Fungible Token Standard
参考
https://github.com/ethereum/EIPs
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
源代码 - https://ethfiddle.com/InrBR4V-KL
//Write your own contracts here. Currently compiles using solc v0.4.15+commit.bbb8e64f.
pragma solidity ^0.4.18;
contract ERC721 {
function ownerOf(uint256) public returns (address);
}
contract ComposableAssetFactory {
// which asset owns which other assets at which address
mapping(uint256 => mapping(address => uint256)) children;
// which address owns which tokens
mapping(uint256 => address) owners;
modifier onlyOwner(uint256 _tokenID) {
require(msg.sender == owners[_tokenID]);
_;
}
function registerOwner(uint256 _tokenID, address _contract) public returns (bool) {
require(owners[_tokenID] == address(0));
ERC721 erc721 = ERC721(_contract);
address owner = erc721.ownerOf(_tokenID);
assert(owner == msg.sender);
owners[_tokenID] = msg.sender;
return true;
}
// change owner of a token
function changeOwner(address _newOwner, uint256 _tokenID) onlyOwner(_tokenID) public returns (bool) {
owners[_tokenID] = _newOwner;
return true;
}
// add erc20 child to a composable asset
function addFungibleChild(uint256 _tokenID, address _childContract, uint256 _amount) onlyOwner(_tokenID) public returns (bool) {
require(
_childContract.call(
bytes4(keccak256("transferFrom(address,address,uint256")),
msg.sender,
this,
_amount
)
);
// set as child
children[_tokenID][_childContract] += _amount;
return true;
}
// add erc721 child to a composable asset
function addNonFungibleChild(uint256 _tokenID, address _childContract, uint256 _index) onlyOwner(_tokenID) public returns (bool) {
require(
_childContract.call(
bytes4(keccak256("transferFrom(address,address,uint256")),
msg.sender,
this,
_index
)
);
// set as child
children[_tokenID][_childContract] = _index;
return true;
}
function transferNonFungibleChild(
address _to,
uint256 _tokenID,
address _childContract,
uint256 _childTokenID
) public onlyOwner(_tokenID) returns (bool) {
require(children[_tokenID][_childContract] == _childTokenID);
require(
_childContract.call(
bytes4(keccak256("transfer(address,uint256)")),
_to, _childTokenID
)
);
children[_tokenID][_childContract] = 0;
return true;
}
function transferFungibleChild(
address _to,
uint256 _tokenID,
address _childContract,
uint256 _amount
) onlyOwner(_tokenID) public {
require(children[_tokenID][_childContract] >= _amount);
require(
_childContract.call(
bytes4(keccak256("transfer(address,uint256)")),
_to, _amount
)
);
children[_tokenID][_childContract] -= _amount;
}
}