剖析 ERC721——了解非同质以太坊代币

摘要:
分析ERC721-了解异构以太坊代币。很多人都听说过以太坊区块链上的一款新游戏,叫做以太坊猫。由于其独特的创意和在以太坊网络上最初的良好表现,这款游戏最近几次成为加密货币社区的头条新闻。在这个游戏中,玩家可以购买、出售、交易和饲养电子猫。

剖析ERC721——了解非同质以太坊代币

1

许多人已经听说过以太坊区块链上的一款新游戏,名为以太猫(CryptoKitties)。由于创意独特新颖并且在以太坊网络上初步表现不错,该款游戏近期在加密货币社区内数次成为头条新闻。在加密猫这款游戏中,玩家可以购买、出售、交易以及饲养电子猫。这些电子猫可被视为“可繁殖的豆豆娃(Beanie Babies,一款玩具公仔的名称),因为每只猫在某种程度上是独一无二的。这种独特性使得加密猫极具收藏价值,因为人们会对好几款猫咪的特征产生兴趣并希望拥有许多只。

2

-来源:Vice Media(一家北美互联网媒体公司)-

然而,可收藏的对象并不限于电子猫。人类一直以来有收藏东西的历史;这没什么新鲜的。从物理货币神奇宝贝卡,人们喜爱收集。这是一种出于对稀缺物品的独特兴趣而形成的习惯。商品价值与其稀缺程度有关,与此类似的是,收藏物对于收藏者的价值与其和其它物品相比起来的稀罕程度相关。

我们能够将以太坊代币看作稀有且具有收藏价值的物品,并且其中每个代币都遵循以太坊社区内被称为ERC721的标准。以太坊代币标准721,或称ERC721,是Dieter Shirley在2017年底引入的一项以太坊改进计划。这项提议标准让智能合约能够像ERC20一样按照可交易代币进行操作。ERC721代币是独一无二的,原因在于这些代币是非同质的

ERC:非同质代币标准 · 问题 #721 · 以太坊/EIPs(编者注:中译本见文末超链接)


同质——拥有后述属性的某个事物(例如金钱或商品):在支付债务或结算时,某个部分或数量可以被另一个同等部分或数量所代替。来源:Merriam-Webster(韦氏词典)


同质性实质上是资产(或本文中的代币)的一种特性,其决定在交易或实用过程中同等或相似类型的物品或数量是否可以完全互换。例如,用下方这张美国的5美元纸币可以从一家便利店购买一瓶汽水。

3

它是有价值的,可以用来购买同等或更少价值的物品。然而,当某个人拿着下面的棒球卡片去购买汽水时,店主不会接受的。

4

-来源:Baseball Card Stars(棒球明星卡)-

当上面的Carlos Santana棒球卡像上面那张纸币一样价值5美元时,为何店主还是不会接受它呢?这是因为这张棒球卡和这张纸币拥有不同的特征,这些特征规定着两者对特定人群的价值。一名7岁的儿童可能愿意花7美元买这张棒球卡,因为他喜欢卡片上的颜色。但是,店主觉得这张卡片5美元都不值,原因很简单,即它不像美元一样是由美联储发行的。棒球卡片和美元纸币拥有的独特属性使它们失去同质性,因为它们依据交易而具有不同的价值并且并不能总是对其进行可互换式使用。

就具有收藏价值的物品而言,如果同一个集合的两个物品具有不同的特征,这两个物品是非同质的。例如物理货币,金币与铜币之间是非同质的,因为两者相异的特征赋予它们的价值对于收藏者来说是不同的。

ERC721代币可以在任何交易所中使用,但是它们的价值取决于和每个代币相关联的独特性及稀缺性。该标准定义了函数 name 、symboltotalSupplybalanceOfownerOfapprovetakeOwnershiptransfertokenOfOwnerByIndex以及tokenMetadata,还定义了两个事件:Transfer(转移)和Approval(授权)。

注意:此为一份ERC721合约样例的简要声明。

contract ERC721 {
   // ERC20 compatible functions
   function name() constant returns (string name);
   function symbol() constant returns (string symbol);
   function totalSupply() constant returns (uint256 totalSupply);
   function balanceOf(address _owner) constant returns (uint balance);
   // Functions that define ownership
   function ownerOf(uint256 _tokenId) constant returns (address owner);
   function approve(address _to, uint256 _tokenId);
   function takeOwnership(uint256 _tokenId);
   function transfer(address _to, uint256 _tokenId);
   function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId);
   // Token metadata
   function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl);
   // Events
   event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
   event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}

合约内各字段概述如下。

谨记:下列编码仅为教学目的,未经测试。请勿在生产应用程序中进行执行!

类似ERC20的函数

ERC721定义的一些函数使其和ERC20代币标准存在一致的地方。这样做的目的在于更加方便现有钱包展示有关于代币的简单信息。这些函数使符合该标准的智能合约表现得像诸如比特币或以太坊之类的普通加密货币,方式在于定义使用户执行诸如将代币发送给其他人和核实账户余额等动作的函数。

函数name

该函数用于告诉外部合约和应用程序该代币的名称。该函数的一项实现示例可以如下。

contract MyNFT {
  function name() constant returns (string name){
    return "My Non-Fungible Token";
  }
}

函数symbol

该函数同时有助于提供与ERC20代币标准的兼容性。其向外部程序提供该代币的简称或标识符。

contract MyNFT {
  function symbol() constant returns (string symbol){
    return "MNFT";
  }
}

函数totalSupply

该函数会返回区块链上可提供的货币的总数量。这种供给不一定要保持不变。

contract MyNFT {
  // This can be an arbitrary number

  uint256 private totalSupply = 1000000000;

  function totalSupply() constant returns (uint256 supply){
    return totalSupply;

  }

}

函数balanceOf

该函数用来获得某个给定地址拥有的代币数量。

contract MyNFT {
  mapping(address => uint) private balances;
  function balanceOf(address _owner) constant returns (uint balance)
  {
    return balances[_owner];
  }
}

所有权函数

这些函数定义了合约处理代币所有权的方式以及转移所有权的方式。其中最值得注意的函数为takeOwnership和transfer,分别担任提取和发送功能,且对用户之间转移代币来说必不可少。

函数ownerOf

该函数返回某代币的所有者的地址。由于每个ERC721代币都是非同质且因此独一无二的,其在区块链上通过一个唯一的ID被查询。我们可以利用其ID判断代币的所有者。

contract MyNFT {
  mapping(uint256 => address) private tokenOwners;
  mapping(uint256 => bool) private tokenExists;

  function ownerOf(uint256 _tokenId)
  constant returns (address owner) {
    require(tokenExists[_tokenId]);

    return tokenOwners[_tokenId];
  }
}

函数approve

该函数批准或授予另一个实体代表所有者转移代币的权利。例如,如果Alice拥有1个MyNFT(我的非同质代币),她可以为朋友Bob调用 approve 函数。调用成功后,Bob稍后就可以代表Alice获得该代币的所有权或对该代币进行操作。关于所有权转移的更多内容可参见函数 takeOwnership 和 transfer 。

contract MyNFT {
  mapping(address => mapping (address => uint256)) allowed;

  function approve(address _to, uint256 _tokenId){
    require(msg.sender == ownerOf(_tokenId));
    require(msg.sender != _to);

    allowed[msg.sender][_to] = _tokenId;
    Approval(msg.sender, _to, _tokenId);
  }
}

函数takeOwnership

该函数担任提现功能,因为某个外部方可以调用它来从另一个用户的账户中提取出代币。因此,在某个用户已经被授权获得一定数额的代币并且希望从另一个用户的余额中提取所述代币的时候,可以使用函数 takeOwnership 。

contract MyNFT {
  function takeOwnership(uint256 _tokenId){
    require(tokenExists[_tokenId]);

    address oldOwner = ownerOf(_tokenId);
    address newOwner = msg.sender;

    require(newOwner != oldOwner);

    require(allowed[oldOwner][newOwner] == _tokenId);
    balances[oldOwner] -= 1;
    tokenOwners[_tokenId] = newOwner;

    balances[newOwner] += 1;
    Transfer(oldOwner, newOwner, _tokenId);

  }
}

函数transfer

转移代币的下一个方法就是使用该函数。函数transfer让代币所有者将其代币发送给另一个用户,与独立加密货币类似。然而,只有收款账户事先被打款账户授予获得代币的权利,转账才能开始。

contract MyNFT {
  mapping(address => mapping(uint256 => uint256)) private ownerTokens;
  function removeFromTokenList(address owner, uint256 _tokenId) private {
    for(uint256 i = 0;ownerTokens[owner][i] != _tokenId;i++){
      ownerTokens[owner][i] = 0;
    }

  }

  function transfer(address _to, uint256 _tokenId){
    address currentOwner = msg.sender;
    address newOwner = _to;

    require(tokenExists[_tokenId]);

    require(currentOwner == ownerOf(_tokenId));
    require(currentOwner != newOwner);
    require(newOwner != address(0));
    removeFromTokenList(_tokenId);

    balances[oldOwner] -= 1;
    tokenOwners[_tokenId] = newOwner;

    balances[newOwner] += 1;
    Transfer(oldOwner, newOwner, _tokenId);
  }
}

函数takenOfOwnerByIndex(可选—推荐)

每个非同质代币所有者可以一次拥有1个以上的代币。每个代币通过唯一的ID来查询,然而,追踪某个用户可能拥有的每个代币也许存在困难。为此,合约记录着每个用户拥有的所有代币的ID。因此,某个用户拥有的每个代币都可以在该用户拥有的代币列表(数组)中通过索引检索到。 tokenOfOwnerByIndex

contract MyNFT {
  mapping(address => mapping(uint256 => uint256)) private ownerTokens;

  function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId){

    return ownerTokens[_owner][_index];
  }
}

元数据函数

就像我们之前说的,非同质物之所以非同质是因为其一系列独特的属性。美元和棒球卡不是同质的,因为它们具有不同的特征。然而,将表明每个代币最典型特征的数据存储在区块链上十分昂贵,我也不建议这样做。为了防止这种情况,我们可以将关联到每个代币属性的索引(如IPFS哈希或HTTP链接)存储在链上,这样一来,链外的程序就可以执行逻辑以获取更多有关该代币的信息。这些索引就是“有关数据的数据”,也可以叫做元数据。

tokenMetadata(可选—推荐)

该函数让我们发现代币的元数据或指向其数据的链接。

contract MyNFT {

  function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl) {
    return tokenLinks[_tokenId];
  }
}

事件

无论合约在何时调用事件,事件都会被触发。一旦事件被触发,就向任何正在监听的程序广播这些事件。外部程序监听区块链事件,从而可以在事件被触发后利用事件提供的信息执行逻辑。ERC721标准定义了以下两个事件。

转帐

该事件在代币被转手时得以触发。当代币的所有权从某个用户转移到另一个用户手上时,该事件被广播。其详细说明了哪个账户发送该代币,哪个账户接收该代币,以及哪个代币(通过ID定义)被转移。

contract MyNFT {
  event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
}

授权

该事件在某个用户授权另一个用户获得某个代币的所有权时(即,当授权被执行时)得以触发。其详细说明了哪个账户目前拥有该代币,哪个账户获许在未来获得该代币,以及哪个代币(通过ID定义)被授权转移其所有权。

contract MyNFT {
  event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}

我执行的ERC721标准源代码可以在这里找到。

和ERC20类似,该新提议的ERC721标准为新型智能合约扮演非同质物品的角色开辟了一条新的道路。就如加密猫Decentraland加密庞克(CryptoPunks)及其他许多应用程序中所见,非同质代币被证明是高需求产品。甚至维基解密都拥有好几只高价值加密猫呢!然而,该标准最终将会进一步扩大加密货币经济并且帮助该领域获得进一步的发展。

免责声明:文章转载自《剖析 ERC721——了解非同质以太坊代币》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vue项目中引用spreadjs方法parseObject方法将json字符串转换成Map下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

从零开始的野路子React/Node(7)将Swagger(OpenAPI)运用于后端API

之前公司做项目是用过swagger来配置python模型的API,感觉非常好用。swagger可以提供request, response甚至error的验证机制,十分便利。node当然也可以用啦。 我们需要使用的库主要是swagger-ui-express,它将提供swagger的相关功能以及一个UI,方便查看和调试。 1、初始设定 老规矩,我们还是通过e...

nodejs入门篇---创建project并具体解释

想了非常久。总想写点对大家有优点的,今天解说生成项目。 如今市面上一般须要人全栈-----mean(mongo,express。angular,nodejs),这样能够从前端开发到后端以及数据库,听起来牛呼呼的。 这篇文章就说nodejs和比較流行的框架express4.X版本号(因为3.X版本号没怎么研究过。并且和4有一定差别。况且4也出来非常久了。...

vue路由懒加载

当打包构建应用时,javascript包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。 结合Vue的异步组件和webpack的code splitting feature,轻松实现路由组件的懒加载。 我们要做的就是把路由对应的组件定义成异步组件: const Foo =...

Node.js安全清单

前言 安全性,总是一个不可忽视的问题。许多人都承认这点,但是却很少有人真的认真地对待它。所以我们列出了这个清单,让你在将你的应用部署到生产环境来给千万用户使用之前,做一个安全检查。 以下列出的安全项,大多都具有普适性,适用于除了Node.js外的各种语言和框架。但是,其中也包含一些用Node.js写的小工具。 配置管理 安全性相关的HTTP头 以下是一些安...

webpack4.x最详细入门讲解

前言 本文主要从webpack4.x入手,会对平时常用的Webpack配置一一讲解,各个功能点都有对应的详细例子,所以本文也比较长,但如果你能动手跟着本文中的例子完整写一次,相信你会觉得Webpack也不过如此。 一、什么是webpack,为什么使用它? 1.1 什么是webpack? 简单来说,它其实就是一个模块打包器。 1.2 为什么使用它? 如果像...

node爬虫爬取中文时乱码问题 | nodejs gb2312、GBK中文乱码解决方法

iconv需要依赖native库,这样一来,在一些不支持native模块安装的虚拟主机和windows平台上,我们还是无法安心处理GBK编码。 老外写了一个通过纯Javascript转换编码的模块 iconv-lite 可以实现window下的转换 ,通过npm可以安装此模块,bufferhelper是一个操作buffer的加强类 首先安装 npm ins...