ERC721

ERC721

目前看,ERC721 跟 ERC20 及其近亲系列有本质上的不同。

ERC721 中,代币都是唯一的。ERC721 是几个月前提出来的方案,CryptoKitties,这款使用ERC721标准实现的收集虚拟猫游戏使得它备受瞩目。以太猫游戏实际就是智能合约中的非同质代币 (non-fungible token),并在游戏中用猫的形象来表现出来。

如果想将一个 ERC20 合约转变成 ERC721 合约,我们需要知道 ERC721 是如何跟踪代币的。

在 ERC20 中,每个地址都有一个账目表,而在 ERC721 合约中,每个地址都有一个代币列表:

mapping(address => uint[]) internal listOfOwnerTokens;

由于 Solidity 自身限制,不支持对队列进行 indexOF() 的操作,我们不得不手动进行队列代币跟踪:

mapping(uint => uint) internal tokenIndexInOwnerArray;

当然可以用自己实现的代码库来发现元素的索引,考虑到索引时间有可能很长,最佳实践还是采用映射方式。

为了更容易跟踪代币,还可以为代币的拥有者设置一个映射表:

mapping(uint => address) internal tokenIdToOwner;

以上就是两个标准之间最大的不同,ERC721 中的 transfer() 函数会为代币设置新的拥有者:

function transfer(address _to, uint _tokenId) public (_tokenId)
{ 
  // we make sure the token exists
  require(tokenIdToOwner[_tokenId] != address(0));
  // the sender owns the token
  require(tokenIdToOwner[_tokenId] == msg.sender);
  // avoid sending it to a 0x0
  require(_to != address(0)); 

  // we remove the token from last owner list
  uint length = listOfOwnerTokens[msg.sender].length; // length of owner tokens
  uint index = tokenIndexInOwnerArray[_tokenId]; // index of token in owner array
  uint swapToken = listOfOwnerTokens[msg.sender][length - 1]; // last token in array

  listOfOwnerTokens[msg.sender][index] = swapToken; // last token pushed to the place of the one that was transferred
  tokenIndexInOwnerArray[swapToken] = index; // update the index of the token we moved

  delete listOfOwnerTokens[msg.sender][length - 1]; // remove the case we emptied
  listOfOwnerTokens[msg.sender].length--; // shorten the array's length

  // We set the new owner of the token
  tokenIdToOwner[_tokenId] = _to;

  // we add the token to the list of the new owner
  listOfOwnerTokens[_to].push(_tokenId);
  tokenIndexInOwnerArray[_tokenId] = listOfOwnerTokens[_to].length - 1;

  Transfer(msg.sender, _to, _tokenId);
}

尽管代码比较长,但却是转移代币流程中必不可少的步骤。

还必须注意,ERC721 也支持 approve() 和 transferFrom() 函数,因此我们必须在 transfer 函数内部加上其它限制指令,这样一来,当某个代币有了新的拥有者,之前的被授权地址就无法其代币进行转移操作,代码如下:

function transfer(address _to, uint _tokenId) public (_tokenId)
{
  // ...
  approvedAddressToTransferTokenId[_tokenId] = address(0);
}

挖矿

基于以上两种标准,可能面对同一种需求,要么产生同质代币,要么产生非同质代币,一般都会用一个叫做 Mint() 的函数完成。

实现以上功能函数的代码如下:

function mint(address _owner, uint256 _tokenId) public (_tokenId)
{
  // We make sure that the token doesn't already exist
  require(tokenIdToOwner[_tokenId] == address(0));

  // We assign the token to someone
  tokenIdToOwner[_tokenId] = _owner;
  listOfOwnerTokens[_owner].push(_tokenId);
  tokenIndexInOwnerArray[_tokenId] = listOfOwnerTokens[_owner].length - 1;

  // We update the total supply of managed tokens by this contract
  totalSupply = totalSupply + 1;

  // We emit an event
  Minted(_owner, _tokenId);
}

用任意一个数字产生一个新代币,根据不同应用场景,一般在合约内部只会授权部分地址可以对它进行挖矿(mint)操作。

这里需要注意,mint() 函数并没有出现在协议标准定义中,而是我们添加上去的,也就是说我们可以对标准进行扩充,添加其它对代币的必要操作。例如,可以添加用以太币来买卖代币的系统,或者删除不再需要代币的功能。

元数据

如前所述,非同质代币是价值的代表,大量情况下,需要描述这种价值。可以用如下字符串实现:

mapping(uint => string) internal referencedMetadata;

由此可见,智能合约与其说内含某种对象不如说是一种权益的证明。例如,不能将一辆车存放在智能合约中,但是可以存放车牌或者其它法律票证。

目前虚拟资产广泛使用的技术都用 IPFS 哈希作为元数据,IPFS 哈希是存放在 IPFS 系统中文件的地址。简单说,IPFS 是一个 HTTP 的 torrent 版本。当一个新文件添加到 IPFS 中,就会在 IPFS 网络中的至少一个计算节点上表现出来。

当文件通过 IPFS 或者 HTTP 对每个人都可见时,“代币所有权证明”就在智能合约中注册。这个操作不是程序,而应该是不可替代代币的一种新应用。它被称为“Crypto-collectibles”,现在变得很热门。

回到我们的代码,ERC721 的讨论目前不太活跃了,原始建议贴很久都没有更新过,因此基于此又有新的讨论方案,被称为 ERC841。在 ERC841 中“不可替代代币(non-fungible token)”被“契据(deed)”的称呼替代。

另外一个方案,ERC821,也被提出来,期望基于 ERC223 和 ERC777 提供更好的方案设计。

ERC821 和 ERC841 有同样的目标,但是实现方法上有些许不同,但都有待改进,如果大家有建议,可以参与讨论。

可以在 Github 上找到 ERC20 和 ERC721 的实现(不建议用于生产),链接为:devzl/ethereum-walkthrough-4

另外,花点儿时间了解 OpenZepplin 框架也是值得的。他们有非常棒的基本上通过了审计的模块化智能合约(当然,在你决定使用哪个模块之前最好通读其内容)

以上就是第四部分的内容,下一篇中我们将介绍如何创建 DApp

如果喜欢本文,可以通过如下方式联系我:@dev_zl

彩蛋

Initial coin offerings 有点儿偏离以太坊项目开发的议题,但是本质上,它就是一种众筹。

如果一个初创公司需要资金,就可以创建自己的代币,过一段时间卖一部分,被称做 crowdsale 或者 Initial coin offering。

在智能合约和区块链技术出现之前,初创公司会使用众筹网站集资,但是这种网站会抽走很大一部分服务费。有了 Initial coin offering 之后,没有了中间商,筹集的钱都归初创公司自己用了。

目前,集资项目更多的是骗局,从投资者角度看,应该看好自己的钱袋。从开发者角度看,crowdsale 就是一种智能合约,它卖的是未来兑换以太币的代币。没有一个标准方式,但是可以从OpenZepplin代码库中找到一些好的实现方式。另外,在以太坊上也有一个简易教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值