《以太坊技术详解与实战》第7章 - 以太坊上数字资产的发行和流通

7.1 以太坊上的数字资产定义

以太坊设计目标就是让各种数字资产以智能合约的形式运行在以太坊虚拟机上 。 目前,在以太坊上的众多智能合约之中,应用最为广泛的是代币合约( Token Contract ) 。 代币合约是在以太坊上管理账户及其拥有的代币的智能合约,实质上可以理解为一张账户地址和对应账户代币余额的映射表。

从某种意义上讲,以太坊上的代币可以被称为数字资产 , 记录资产数据的代币合约就是一份账本 。 代币既可以表示某一虚拟货币的价值,又可以象征某些实际的物理量,甚至可用于记录账户持有者的信誉值。但归根结底,以太坊上的数字资产就是指以太坊上代币合约中记录的账户代币余额数据。

与以往搭建由区块链直接记录的加密货币(Cryptocurrency)不同,以太坊上的数字资产( Digital Asseton Ethereum )是记录在以太坊之上的代币合约中。 一般意义上的加密货币,如比特币、以太币和莱特币等,是记录在账户状态中,直接存储在区块内的数据 ,伴随“挖矿”等机制发行,井通过交易的方式流通。而数字资产则是以以太坊区块链为平台,记录在更高一层的代币合约中,具体来讲是存储在以太坊交易消息数据字段的可执行代码中 。数字资产的发行无须经过复杂的“挖矿”程序,代币合约的创建者可以通过智能合约定义自己的代币发行标准,直接在合约代码中实现“铸币”功能 。 并且,代币的流通是通过在以太坊交易中调用智能合约的函数接口进行转账, 代币合约创建者同样可以在这一过程中添加一些自定义的操作 。相比之下,以太坊上的数字资产较加密货币拥有更高的灵活性,并且其安全性也由以太坊区块链机制和智能合约代码保证。

7.2 发行和流通

以太坊上的代币作为一种数字资产,需要有它的发行和流通机制 。相较于以太币等加密货币基于 PoW 和 PoS 共识算法的发行机制以及基于发送交易进行转账的流通机制,以太坊上数字资产的发行和流通更加简便灵活,相关的操作一般由代币合约创建者在代币合约中实现 。 常见的代币合约在记录账户及代币余额的基础上,还包含一些基本的数字资产管理操作,如铸造代币、销毁代币以及代币转账等。代币转账是代币合约的一项基本功能,也是数字资产流通功能的具体实现。 例如,账户 Alice 调用合约的转账功能函数,向账户 Bob 转入 50 个代币,此时合约中记录的 Alice 账户余额减少了 50 ,而 Bob 账户余额增加了 50 。 代币合约通过铸造代币和销毁代币来增加或减少代币供应总量,这两者是数字资产发行和回收功能的具体实现。 当其他账户通过向合约转入以太币或其他方式调用合约铸造功能时,该代币合约向账户对应的余额值增加相应数量的代币,代币的总供应盘也相应增加,完成铸币。 例如,账户 Charlie 调用代币合约的铸币功能函数,合约经验证后在将其余额增加 50 个代币,同时代币总供应量也增加 50 个代币 。 与之类似,账户通过调用合约的销毁功能函数,销毁其账户余额中的代币,代币总供应量也相应地减少 。 但是,通常代币合约的代币销毁功能是通过向特殊的零地址 0x000…0000 中转入相应数量的代币来完成,此时代币总供应量不会减少,选择这一操作的原因将在 7.3.2 节中详细介绍。

除了以上的转账、铸币、销毁等基本功能,代币合约还可以加入数字资产的查询、权限控制,甚至经济学公式计算等功能 。 那么,功能繁多的代币智能合约是否有共同点,或者对代币合约的基本功能进行一些规范呢?以太坊开发人员在以太坊改进计划( Ethereum Improvement Proposal, EIP )中提出了 ERC 20 代币合约标准,为以太坊代币合约制定了一套标准的接口。

7.3 ERC 20 代币合约标准

ERC 20 代币合约标准规定了一个以太坊代币合约所需实现的函数功能和事件记录 。 该标准满足了代币作为数字资产所必须具备的一些基本功能和要求,如注明代币名称 、 代币转账、本账户中允许链上第三方使用的代币限额等 。 ERC 20 的出现为以太坊上的代币合约提供了一个标准化的方案,也对以太坊上数字资产的实现进行了一定的规范 。 ERC 20 标准使得种类繁多的代币能够被更多的去中心化应用( DApp )、交易所等兼容。

7.3.1 标准定义
ERC 20 标准接口如下 。 我们将在下一节对各个接口做详细的介绍。

contract ERC20  {
	string public constant name =”Token Name";
	string public constant symbol =”SYM”;
	uint8 public constant decimals = 18;
	
	function totalSupply() constant returns (uint supply);
	function balanceOf( address who ) constant returns (uint value);
	function allowance( address owner, address spender ) constant returns (uint _allowance) ;
	function transfer( address to, uint value) returns (bool ok);
	function transferFrom( address from, address to, uint value) returns (bool ok);
	function approve( address spender, uint value ) returns (bool ok);
	
	event Transfer( address indexed from, address indexed to, uint value);
	event Approval( address indexed owner, address indexed spender, uint value);
}

7.3.2 ERC 20 标准接口
自 2015 年提出以来, ERC 20 代币合约标准在以太坊开发社区的协商下逐步确定为 7.3.1 节所展示的 11 个标准接口 。 这一标准的出现为 2017 年以太坊合约代币井喷式发展打下了基础。 ERC 20 标准接口总共可分为三类:常量 、功能函数以及事件,本节将按照分类对各个接口进行逐一介绍。

1 . 常量
常量类接口包含代币名称、代币符号和小数点位三个常量 ,分别定义了合约代币的名称 、 符号等基本信息 。
( 1 )代币名称
代币名称( name )是由代币合约创建者指定的完整名称,是一串公开的字符串常量,如CarToken 。 尽管在以太坊上,代币名称是由各代币合约各自指定,无法保证一个代币名称唯一标识一种特定的合约代币 。 但在代币交易所中,符合 ERC 20 标准的代币可以向交易所提供代币名称进行注册,交易所通过注册机制可以检查并保证代币名称与代币合约一一对应,实现类似于 ENS 的效果 。
( 2 )代币符号
代币符号( symbol )是由代币合约创建者指定的代币简称,是一串公开的字符串常量,一般由 3 ~ 4 个大写字母组成,便于标识该代币,如 EXT 。 与代币名称相同 ,符合 ERC 20 标准的代币可以通过在各交易所中注册,使其代币符号能够唯一标识该代币合约 。
( 3 )小数点位
小数点位( decimals )是由代币合约创建者指定的一个公开的无符号整数常量,用于指定代币的最小精度值, 一般为 18 。 小数点位的数值表示该代币在交易中最小单位在小数点后的位数,比如 18 表示该代币在交易中的最小单位为 1 × 10 ^ 18 个代币 。实际上,之所以要引人这一常量 ,是因为以太坊虚拟机不支持小数计算,智能合约代码中涉及小数的数值只能先转换为整数后再进行计算 。 代币合约中设置最小精度值后,合约代码中的数值计算便可以先乘 10 的小数点位次乘方转换为整数,再送入 EVM 进行计算,最终结果还原为小数,确保了合约中数值计算的精确性 。
另外,需要指出的是,由于以太币本身的小数点位设置为 18 ,因此符合 ERC 20 标准的代币一般将小数点位设置为 18 。 尽管如此,小数点位仍可以根据实际需求进行调整 。 当代币的数额计算中要求更高精确度时,小数点位可以设置得更高 。 当数字资产用于表示一些无法分割的实际物品,如软件证书时,代币的最小单位应为 1 ,此时小数点位应设为 0 。

2.函数功能
函数功能包含总供应量、余额、转账、从他人处转账、允许量值以及限额 6 个功能函数,分别规定了实现代币合约所必需的查询、转账 、 权限控制等基本功能的函数接口 。

( 1 )总供应量
总供应量 totalSupply( )函数用于查看代币当前的总供应量,即当前合约账本中所有账户余额的总和 。 该函数没有输入参数,返回值为无符号整数常量 。
( 2 )余额
余额 balanceOf( )函数用于查看当前合约中指定账户的代币余额 。 该函数的输入参数为账户地址,返回值为账户代币余额,为无符号整数常量 。
( 3 )转账
转账 transfer( )函数用于从当前账户向其他账户进行代币转账 。 该函数的输入参数为目标账户地址和转账的代币数额,返回值为布尔型变量 。 当账户满足当前有足够的余额 、 转账数额为正数以及合约编写者指定的其他条件时,转账成功,则合约中当前账户的余额减少,目标账户中的余额增加,函数返回值为真;否则转账失败,函数返回值为假 。
( 4 )从他人处转账
从他人处转账 transferFrom( )函数用于从他人账户向其他账户进行代币转账。 在有些情况下,用户不仅可以使用 transfer( )函数自己发起转账,还可以授权他人在一定限额下调用transferFrom( )函数从自己账户中转出代币,而无须自己介入 。 例如,在一个银行合约中,由于合约无法控制用户的行为,不能命令用户使用 transfer( )发起转账,但可以由用户提前授权,并通过 transferFrom( )从用户账户中转出钱款,自动完成转账过程,而无须通知用户参与 。 该函数的输入参数为转账的发起地址、目标地址以及转账数额 。 与 transfer( )函数一样,当转账成功时返回值为真,转账失败则返回值为假 。
( 5 )允许量值
允许量值 approve()函数用于设定当前账户对指定账户的允许转账量值( allowed ) 。 该函数的输入参数为代币使用方地址和允许使用的额度,返回值为设置是否成功的布尔型变量 。 ERC 20 标准为了满足更广泛的需求,提供了 transferFrom( )接口 。 为了账户更方便地管理自己的账户余额,必须对其他人从本账户中转走的代币数额进行限制,于是 ERC 20 标准引入了允许量值 allowed 。 allowed 是一个二元组, allowed[A][B]记录的是用户 A 对本账户中允许账户 B 转走的代币额度 。 用户 A 通过调用 approve()函数并指定账户 B 和允许额度,对 allowed[A][B]进行设置;当账户 B 调用 transferFrom()函数从账户 A 中转出代币时,需先通过检查,确保转出的数额不超过账户 A 设置的 allowed[A][B]值,并且转账之后allowed[A][B]值会减少相应的数额 。
( 6 )限额
限额 allowance( )函数用于查看当前的 allowed 值 。 该函数的输入参数为代币持有方A 的地址和代币使用方 B 的地址,返回值为当前在账户 A 中允许账户 B 转出的代币数额allowed[A][B],为无符号整型常量 。

3.事件
智能合约中还包括了记录事件的event 类型接口, ERC 20 合约标准也对代币合约基本的事件接口进行了规范 。 ERC20 标准要求代币合约包含至少两个事件:转账( Transfer )和 允许( Approval ) 。
( 1 )转账
Transfer()事件用于记录代币合约最基本的功能一一转账 。 事件的输入参数为转账的发起方、接收方以及转账的代币金额,一般位于 transfer( )函数和 transferFrom( )函数中转账成功之后触发 。 用户可以从交易收据( receipt)中查看每一笔代币转账的相关信息 。
( 2 )允许
Approval( )事件用于记录代币合约的进阶功能一一允许他人从本账户中转出代币。事件的输入是代币的持有者、使用者以及所设置的允许金额,一般位于 approve()函数中,设置允许限额成功之后触发 。 用户可以从交易收据( receipt)中查看代币持有者对他人设置的允许转账限额等相关信息 。

7.3.3 现有 ERC 20 标准代币
随着智能合约和代币的兴起,以及 ERC 20 代币合约标准的提出,以太坊上涌现出了大量符合 ERC 20 标准的代币,如 EOS 、 BAT 和 REP 等 。 目前以太坊上市场份额较大的代币主要是由 DApp 发行的代币或者开发其他种类区块链之前众筹而发行的代币 。

( 1 ) EOS 代币
EOS 代币是由 Daniel Larime 等人开发的 EOS . IO 项目所发行的一种代币 。 EOS.IO 是一款新一代的区块链项目,针对以太坊现有的一些问题作出改进 。

( 2 ) BAT 代币
BAT 代币( Basic Attention Token )是以太坊上一款数字广告平台 DApp 在 2017 年 5 月所发行的一种 ERC 20 代币 。 该 DApp 将前端浏览器上统计的用户网页广告停留时间换算成用户的“注意力”(attention),依此转换成 BAT 代币的价值,并让广告主由此向广告发行商支付相应的 BAT 代币。

( 3 ) REP 代币
REP 代币是由 Augur DApp 项目 2017 年 7 月发行的一种代币 。 Augur 是一个用于预测未来事件的、基于以太坊区块链的市场预测平台,用户可以通过该平台对未来的事件进行预测并押注,若预测正确则可以获得奖励,否则会有一定的损失 。Augur 平台的智能合约中包括了一套 ERC 20 标准代币合约,其代币 REP 可用于预测过程中的交易 。 相比于一般的市场预测平台, Augur 将平台搭建于以太坊区块链之上,使用智能合约进行管理,实现了去中心化,更加安全可靠,井且使用符合 ERC 20 标准的 REP 代币,提高市场交易效率,增强了流通性 。

下面以 EOS 代币为例,简要介绍 ERC 20 标准在 EOS 代币合约中的具体实现。
在 EOS 代币的合约中, ERC 20 标准接口的具体实现位于合约 DSTokenBase 中 。 其中,ERC 20 标准中的基本功能一一transfer (转账)函数如下 。 transfer 函数的输入参数为转账接收方地址 dst 和转账金额 wad 。 函数首先判断转账发送方,即转账消息的发起方 msg.sender的余额是否足够;然后发送方 msg .sender 余额减少,接收方 dst 余额增加;再触发 Transfer 事件记录转账的 msg.sender 、 dst 和 wad ;最后,转账成功,返回真值。

function transfer(address dst, uint wad) returns (bool) {
	assert(_balances[msg.sender] >= wad);
	_balances[msg.sender] = sub( _balances[msg.sender], wad);
	_balances[dst] = add( _balances[dst], wad);
	Transfer(msg.sender, dst, wad);
	return true;
}

ERC 20 中更进一步的转账功能一一transferFrom 函数 。 函数的输入参数为转账的发送方 src 、接收方 dst 和金额 wad 。 transferFrom 函数转账的流程如下:
1 )除了判断发送方余额是否足够之外,还需判断发送方 src 对转账发起者 msg.sender 的允许限额 _approvals 是否足够;
2 )转账过程中,除了发送方、接收方的余额变更之外,还需将发送方对转账发起者的允许限额_approvals[src] [msg.sender]扣除相应的转账数额 ;
3 )同样触发 Transfer 事件进行记录;
4 )转账成功,返回真值 。

function transferFrom(address src, address dst, uint wad) returns (bool) {
	assert(_balances (src] >= wad) ;
	assert(_approvals[src] [msg.sender] >= wad);
	_approvals[src] [msg.sender] = sub(_approvals[src] [msg.sender], wad);
	_balances[src] = sub(_balances[src], wad);
	_balances[dst] = add(_balances[dst], wad);
	Transfer(src, dst, wad);
	return true;
}

此外, ERC 20 中还有一个重要的功能一一设置允许限额(_approvals ) 。 函数除了将调用者 msg.sender 对被授权方 guy 的允许值_approvals 进行更改之外,还触发了 Approval 事件来记录授权方 msg.sender 、 被授权方 guy 和允许限额 wad ,并返回真值。 approve 函数的实现如下 。

function approve(address guy, uint256 wad) returns (bool) {
	_approvals[msg.sender][guy] =wad;
	Approval(msg.sender, guy, wad);
	return true;
}

除了 ERC 20 标准核心函数的实现外,在合约 DSToken 中也包括了代币基本的发行功能一一铸币( mint)和销毁( burn ),具体函数实现如下 。

function mint(uintl28 wad) auth stoppable note {
	_balances[msg.sender] = add(_balances[msg.sender], wad);
	_supply = add(_supply, wad);
}

function burn(uint128 wad) auth stoppable note {
	_balances[msg.sender] = sub(_balances[msg.sender], wad);
	_supply = sub(_supply, wad)
}

两个函数均带有 auth 函数修改器,限定了只能由合约创建者、合约所有者或由创建者授权过的账户调用。 铸币 mint 函数根据输入的铸币金额 wad ,在消息发送方 msg.sender 的账户余额以及代币总供应量 _supply 的数额上直接增加 wad 数额的代币 。 与之相反,销毁 burn 函数则是在账户余额以及代币总供应量中减少相应数额的代币。

7.4 ERC 721 代币合约标准

除了最为通用的 ERC 20 标准,目前有许多人在此基础上不断提出功能更为全面 、 内容更为细致的合约标准如 ERC 721 、 ERC 223 、 ERC 644 以及 ERC 677 等 。 本节主要介绍ERC 721 合约标准 。

7.4.1 标准定义
ERC 721 合约标准规定了一种不可替代的代币 ( Non-fungible Token, NFT )的合约接口 。 此类代币的最小单位为个,即在 ERC 20 标准中对应小数点位的 decimal 值为零 。 此类代币最重要的特点为每一个代币都是独一无二的 。 每一个代币拥有各自的 tokenld 标号,并且可以附上一些各不相同的特征值,这样使得每个代币都是“不可替代”的 。 ERC 721代币合约标准的接口如下 。

contract ERC721 {
	//Required method
	function totalSupply() constant returns (uint256 totalSupply);
	function balanceOf(address owner) constant returns (uint256 balance);
	function owner0f(uint256 tokenid) constant returns (address owner);
	function approve(address _to, uint256 _tokenid);
	function takeOwnership(uint256 tokenid);
	function transfer(address to, uint256 tokenid);
	
	//Optional method
	function name() constant returns (string name);
	function symbol() constant returns (string symbol);
	function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns	(uint tokenId);
	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);
}

ERC 721 标准继承了 ERC 20 标准的一些基本功能接口,如 name() 、 symbol()、totalSupply( ) 、 balanceOf(),以及事件 Transfer( ) 、 Approval() 等 ,并在 一 些函数,如transfer() 、 approve()中加入了_tokenld 用以指定特定的代币。

除此之外, ERC 721 相比于 ERC 20 还新增了 一些功能函数。其中,ownerOf()和 tokenOfOwnerBylndex()分别为根据代币 ID 查询该代币的持有者,以及根据持有者及其索引查询所持有的代币 ID; ERC 20 中的 transferFrom( )方法被更改为 takeOwnership( ),在限额 approve 允许的条件下,交易发起方 msg.sender 调用该函数可以将指定 _tokenld 的代币从他人处转至自己的账户中; tokenMetadata( )函数用于查看代币的元数据等 ,根据代币的 ID 查询到一个 URL 格式字符串,其中包含这一代币的名称 、 图像和描述等相关信息 。

7.4.2 CryptoKitties DApp

7.5 本章小结

相比于普通的数字加密货币,以太坊上的数字资产利用了以太坊智能合约的灵活性,既保持了去中心化和安全等特性,又具有轻量化以及更强的流通性等特点 。 本章首先从数字资产的定义及其发行和流通的角度介绍了其概念和基本功能;然后深入 ERC 20 代币合约标准,详细介绍了代币合约的基本功能和对应的标准接口;最后结合 CryptoKitties 养猫 DApp 介绍了目前较为小众的 ERC 721 合约标准,展现了以太坊智能合约的灵活性以及以太坊上数字资产更多的可能性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值