编者按:本文来自以太坊爱好者,作者:JimMcDonald,翻译&校对:闵敏&阿剑,Odaily星球日报经授权转载。ERC-777是一种新的代币合约标准,解决了ERC-20的一些安全问题,可以让合约创建者、代币持有者和受众在无需更改代币合约的情况下扩展其功能。ERC-777从ERC-20和ERC-223等标准处汲取了很多想法,并在此基础上发展成为新一代标准,为开发者和用户提供了很多强大的特性。本文主要介绍了代币合约,并讲解了ERC-777代币合约的特性、功能和用途。请注意,本文不涉及ERC-20。如果你想了解关于ERC-20代币合约的信息,可以看看另一篇文章。什么是代币合约?
代币合约指的是一个包含了一组账户地址及其对应余额的智能合约,如下图所示。余额表示由合约创建者定义的一种价值:代币合约可以使用余额来表示实物、币值或持币者的声誉。一个单位的余额就是我们通常所说的代币。
-图1:地址及其代币余额表-需要注意的是,一个最终用户可能会拥有任意多个地址。造成这种情况的原因有很多,例如,用户想把自己所持有的代币分散到不同的逻辑账户中,或使用不同的账户来代表不同的来源。每当代币从一个账户转移到另一个账户,代币合约就会更新这两个账户的余额。例如,从0x2299…3ab7向0x1f59…3492转移10个代币之后,余额更新情况如下表所示:
-图2:从0x2299…3ab70向0x1f59…3492转移10个代币之后,余额的变化情况如标红处所示-可以通过铸成新的代币来增加其总供应量。例如,在0x4ba5..ae22中铸成100个代币,则余额的更新情况如下表所示:
-图3:在0x4ba5..ae22中铸成100个代币,则余额的变化情况如标红处所示-代币的总供给量可以通过销毁已有代币来减少。例如,0x4919…413d销毁了50个代币之后,余额的变化情况如下表所示:
-图4:销毁0x4919…431d中的50个代币之后,余额的变化情况如标红处所示-简单的代币合约会将上述信息保存在地址与余额的映射表中。如果是在更复杂的场景下,如分红等,通常会另外采用更加强大的结构。然而,无论具体的实现细节如何,代币余额情况始终如上文的图表所示。ERC-777代币合约的运营者
ERC-777代币合约引入了运营者的概念。运营者是代表代币持有者进行操作的第三方,可以将代币从持有者的地址转移出来。请注意,由于运营者拥有很大权力,应该谨慎添加。每个地址都包含一个经授权的运营者列表,如上表所示:
-图5:代币持有地址的运营者-如上表所示,代币持有地址0x1f59…3492拥有两个运营者,其他两个地址各自拥有一个运营者。当然了,没有运营者的地址也是有效的。使用运营者的一个简单例子是,用户在多个地址代币上都有代币,必须分别管理这些地址。在一般情况下,在将代币从一个地址发送到另一个地址之前,要先确保发送者的地址内有一笔ETH,足以用来支付gas费用。因此,在将ETH从一个账户发送到另一个账户之时,发送者需要先完成几个交易,如下图所示:
-图6:发送代币之前先往账户打钱-如上图所示,地址0x93f1…1b09先向地址0x1f59…3492发送ETH,等待该交易完成后,地址0x1f59…3492向0x4ba5…ae22发送代币。如此繁多的步骤既降低了用户体验,又加大了网络负载量。有了运营者之后,只要一个账户里有ETH,其他账户里有其他代币,即可由持有ETH的账户进行代币转账。接着上一个例子往下看,如果把0x93f1…1b09作为0x1f59…3492的运营者,那么由0x1f59…3492向0x4ba5…ae22发送代币的过程就可以简化为:
-图7:代表另一个账户发送代币-这就大大降低了用户的负担。此外,这可以让用户在通过一个运营者账户控制ETH资金的同时,确保其代币分散于多个持币账户之中。运营者也有可能是合约的形式,而且在代币合约创建的时候就可以为所有持币者预先定义好代币运营者合约。这样一来,运营者可以为所有持币用户提供服务,同时其功能又被限制在智能合约的功能范围内,代币合约就可以不费吹灰之力地为持有者提供更多功能。后文将详细阐述代币合约运营者拥有哪些权力。ERC-777代币合约的定义
每个部署到以太坊上的ERC-777代币合约都会被分配一个地址,即代币地址。这个代币合约将会包含一些定义合约操作的参数。首先要理解的是,由于代币合约缺少一个中心化的注册表,无法保证名称或符号具有唯一性。这样一来,获得并保有唯一身份的最佳方法就是公开你的代币合约。一旦你创建了一个代币合约,就应该把它添加到一些常用网站上,如Etherscan、MyEtherWallet、MyCrypto和CoinMarketCap等等,不过要确保遵守每个网站的要求,这样你的提交得到接受的可能性才最大。代币合约的name就是用来指代合约本身的长名称,例如“Mytoken”。名称的长度并没有限制,但是一些钱包应用可能会将过长的名称截短,因此要将名称的长度控制在较短的范围内。代币合约的symbol就是用来指代合约本身的短符号,例如“MYT”。这个符号跟股票代码差不多,虽然没有长度限制,但是通常都在3至4个字符左右。Solidity不支持小数,但是可分割性对于代币来说是一个常见需求。ERC-777采用的解决方案是,所有代币的内部所示数额均使用其实际数额的1018整数倍来表示。例如,终端用户看到的1.2345代币其实在内部是用1.2345×1018来表示的。这样一来,即使一个代币被分割成了0.000000000000000001,在内部也依旧是以整数表示的,如下表所示:
-图8:代币内部所示数额是终端用户所见数额的1018-一些代币合约的创建者可能不想让他们的代币分割得这么细。例如,有一名用户创建了一个软件许可证代币合约,他可能不想看见一个完整的许可证被分割的情况。又或者,有一名用户创建了一个黄金代币合约,用1token代表1Kg黄金,他可能想将转账金额限制在0.01Kg及以上。代币合约的granularity是代币内部所示数额的最小可分割单位。紧接着上面的例子,许可证代币的粒度应当是1018,而黄金代币的粒度应当是1016。可以预期的是,绝大多数代币合约的粒度都是1,也就是说,这个代币可以被分割成1/1018,或是0.000000000000000001。根据上面给出的例子,如果对代币的可分割性有具体要求,可以选择不同的粒度。不妨来探究一下ERC-777的粒度和ERC-20的小数之间的区别。虽然二者的都可以实现代币的可分割性,ERC-20是基于具体的值来移动小数点的位置,而ERC-777的小数点位置是固定的。这样一来,ERC-777代币的值就更容易在用户界面上显示,因为小数点的位置始终是固定的,只是去掉了后面一连串的0。ERC-777代币合约的功能
ERC-777代币合约具有很多功能,可以让用户查找账户余额,并且在不同条件下将代币从一个账户转到另一个账户上。这些函数的详情如下。totalSupply()函数说明了所有地址持有的代币总量。如果有新的代币被铸造出来,这个值就会增加,如果已有的代币被销毁,这个值就会减少。balanceOf()函数说明了特定地址所持有的代币数量。要注意的是,任何人都可以询问任意地址的余额,因为区块链上的所有数据都是公开的。send()函数将一定数量的代币从信息发送方的地址转到另一个地址上。相比于ERC-20代币,ERC-777代币的发送功能更完善,详情见后文。burn()函数会销毁信息发送方所持有的一部分代币。相比于ERC-20代币,ERC-777代币的销毁功能更完善,详情见后文。authorizeOperator()函数允许消息发送方将自己的代币授权给另一个地址。revokeOperator()函数可以将撤销现有运营者控制消息发送方代币的权限。isOperatorFor()说明了某个地址是否是某个代币持有者的运营者。只要发送方拥有某个账户的运营者权限,就可以通过operatorSend()函数将一定数量的代币从那个账户发送至另一个账户。defaultOperators()函数提供了一个代币运营者合约列表,列表中的运营者均已得到所有代币的全部权限;关于这个功能,可以参见下文的“代币运营者合约”一节。ERC-777代币合约所涉及的事件
ERC-777定义了一些事件,可以用来追踪一个代币合约的个体和整体信息。一旦有新的代币被铸造出来,就会触发Minted()事件。该事件包含了新铸代币的数量,及其目标地址的信息。一旦现有代币被销毁,就会触发Burned()事件。该事件包含了被销毁代币的数量及源地址的信息。一旦有代币从一个地址转移到另一个地址,就会触发Sent()事件。该事件包含了被转移代币的数量,以及持有者地址和接收者地址的信息。除了上述几个事件之外,ERC-777代币标准还包含了两个管理型事件。一旦用户为己方地址添加了一个运营者,就会触发AuthorizedOperator()事件。一旦用户将己方地址的某个运营者移除,就会触发RevokedOperator()事件。要注意的是,这些事件不会包含关于代币数量和所有权变化的信息。详解ERC-777代币合约的发送功能
将ERC-777代币从一个地址发送到另一个地址需要经过几个步骤来完成。在这一流程,ERC-777代币标准在功能性和安全性上都展现出了优越之处。常见的代币发送流程如下图所示:
-图9:常见的代币发送流程-具体步骤如下:验证:确保输入参数是有效的,需验证该地址是否有足额代币可用来发送,以及所发送数额是否是该代币粒度的倍数授权:确保发送方有权发送代币,发送方必须是这些代币的持有者或是拥有对应地址权限的运营者发送:执行代币转账,更新代币合约上每个地址的持币信息日志:发送包含所有操作细节的事件ERC-777在上述步骤的基础上又新增了两个步骤,如下图所示:
-图10:ERC-777的代币发送流程-可以看到,ERC-777在常见流程中新增了tokensToSend()和tokensReceived()这两个步骤。tokensToSend()的调用放在了验证交易信息以及完成授权之后,但是在更新合约地址的持币信息之前。tokensReceived()的调用放在了更新合约地址的持币信息之后。乍一看,新增的步骤似乎没有让整个流程变得很复杂。但是,tokensToSend()和tokensReceived()的强大之处在于,它们不是由合约地址定义的,而是分别位于代币发送方和接收方的合约内。由此一来,发送方和接收方就有权决定是否要达成交易,还可以实现更高级的功能。
-图11:位于不同合约内的tokensToSend()和TokensReceived()-tokensToSend()允许持币者以“在代币离开该账户之前”的形式提供条件和操作。tokensReceived()允许代币接收方以“代币何时到达该账户...”的形式提供条件和操作。tokensToSend()的目的
想象一个场景。假设有一家公司的首席财务官制定了多种货币资金的转出规则。这个首席财务官允许财务经理在遵守公司规则的情况下使用资金,同时保留对规则以及资金的控制权,就如下图所示:
如果这笔资金采用的是ERC-777代币的形式,就可轻松实现上图中的设置。需要执行以下步骤:首席财务官制定的规则被编码进了一个代币控制合约内,且该合约被应用于该公司的持币地址首席财务官授权财务经理成为公司持币地址的运营者财务经理使用operatorSend()发送资金首席财务官可以制定哪些规则?几乎所有规则都可以被编码进智能合约内,下面举了几个例子:只允许运营者花费一定量的资金对运营者设置每日/每周/每月的支出限额运营者只能向一组经过授权的收款方发送资金只有提供了对首席财务官批准过的发票的引用,运营者才可以花费这笔资金等等要留意的一点是,既可以针对公司所持有的不同种类的ERC-777代币制定多套规则,也可以对多种ERC-777代币实行同一套规则。这样一来,首席财务官就可以制定合乎自己公司情况的规则了,财务经理也只能遵守这些规则。同一个地址也可以拥有多个运营者。因此,如果财务经理有代理人的话,只要其代理人也遵守同样的规则,就有权访问这笔资金。tokensToSend()旨在通过对交易制定规则来控制一个或多个账户内资金的转出。说白了,tokensToSend()就是让用户把“在代币离开我的账户之前...”这句话补充完整。这些规则是在代币控制合约中定义的。同一个代币控制合约可用于多个ERC-777代币合约,以及多个账户之间,从而保证各账户之间规则的统一。tokensReceived()的目的
与tokensToSend()类似,tokensReceived()会收到代币已转入该账户的通知。接着上文的例子,该公司有一个会计部门负责付款。每次收到付款,都需要核对是否与发票相符,并且分配给公司内部的部门。收到这笔资金之后,会计部门需要执行以下步骤:如果收到的资金带有发票参考号,则将其与发票进行核对,并记入相应部门的贷方如果收到的资金来自一个已知的发送方,则直接记入相应部门的贷方除上述情况之外,将资金存入持币账户并进行调查就tokensToSend()而言,上述过程只是一个实例,它实际上可以描述一切规则。例如,提早付款可享折扣,只接受哪几种货币,等等。这只需要一个步骤:.该部门的流程被编程进了一个代币控制合约,且该合约被应用于该公司的收款地址通常来说,tokensReceived()之所以没有tokensToSend()那么复杂,是因为两个原因。第一,tokensReceived()通常只包含一个参与者,而tokensToSend()会涉及运营者。第二,相比收款来说,用户通常更关注付款。尽管如此,tokensReceived()是一个非常强大的功能,可以帮助像交易所之类的大型组织来管理已收到的资金。tokensReceived()旨在通过对交易制定规则来控制进入一个或多个账户的资金。说白了,tokensReceived()就是让用户把“当代币进入我的账户之时...”这句话补充完整。这些规则都是在代币控制合约中定义的。同一个代币控制合约可用于多个ERC-777代币合约,以及多个账户之间,从而保证各账户之间规则的统一。对tokensToSend()和tokensReceived()的要求
tokensToSend()是可选项;如果不选的话,就会按照常见流程发送代币。tokensReceived()也是可选的,除非收款账户是合约,在这种情况下就是强制的。强制所有收到代币的合约执行tokensReceived(),就可以确保代币只会被发送到主动说明可以处理这这些代币的合约处。这是ERC-223的主要目标。除此之外,ERC-777还实施了其他保障措施,就是强制收款方登记是否能够接收ERC-777代币和ERC-1820代币。代币运营者合约
如上文所述,代币运营者合约就是在ERC-777代币合约上调用operatorSend()的合约。这类合约的强大之处就在于,它们能够在不需要改变ERC-777代币合约本身的情况下扩展ERC-777的功能。当持币者想要把代币发送到另一个地址之时,他会直接在该代币合约上调用send(),如下图所示:
-图13:直接发送代币-但是,任何用户也都可以通过调用代币运营者合约来发送代币。通过该合约,任何用户都可以代表持有者发送代币,如下图所示:
-图14:通过代币运营者合约发送代币-在创建代币合约之时,就可为所有持有者都启用代币运营者合约,或是在有需要的情况下,为个别持有者启用该合约。代币持有者合约可以为代币持有者提供额外的功能。例如,批量发送代币是一大常见需求,但是没有在ERC-777标准中注明。在部署ERC-777代币合约之前,有可能会新增批量发送的功能,但是这样会为代币合约引入自定义属性,因此更容易出现错误。另一种解决方案是,编写一个可实现批量发送的独立代币运营者合约,并单独进行部署。这个代币运营者合约可以接受来自持币者的交易,并根据交易中所记录的将哪种代币发送给哪些收款方的细节,反复调用operatorSend()来发送这些代币。为了实现批量转账的功能,在部署一个标准ERC-777代币合约的同时会指定一个批量发送运营者合约作为默认的运营者。现在,任何持币者都可以在代币运营者合约上调用send()函数,仅通过一个交易就可以将多种代币从自己的账户上发送出去。如果代币合约没有注明将批量发送运营者合约作为默认的运营者合约,则持币者可以针对账户进行自定义配置。请注意,由上图可见,代币运营者合约只有一个send()函数,但是复杂的代币运营者合约可以有多个send()函数。例如,一个批量发送代币运营者合约可能具备以下功能,即,向多个收款方发送相同数量的代币,向多个收款方发送不同数量的代币,等等。除了上述例子中提到的功能之外,调用代币运营者合约的用户也可以是除持币者之外的人。让外人代替持币者发送代币,这种做法可能听起来很危险,但实际上在很多场景下都非常有用。将持币者和要求转账的用户分开,就可以实现更多功能,例如:在获得持币者授权的情况下发送代币通过发送代币来换取其他代币一旦满足特定条件,立即发送代币简言之,代币运营者合约可以通过修改规则来规定代币在何时可以从一个账户转移到另一个账户。这是一个非常强大的功能,需要用户充分信任代币运营者合约。可以设想的是,以太坊主网上将会部署一些知名的代币运营者合约,用来实现特定的功能。代币合约创建者和个人持币者通过选择自己想要的代币运营者合约就可以扩展功能,从而提高代币转账的效率和安全性。在下一篇文章中,我们将更深入地探究代币运营者合约。代币运营者合约和代币控制合约之间的差别
乍一看,代币运营者合约跟代币控制合约中的tokensToSend()差异不大,其实二者还是有一些差别的。代币运营者合约是可选的;任何持币者都可以忽视这个功能,直接调用send()。而代币控制合约是强制性的,不能被忽视。任何人都可以调用代币运营者合约。代币控制合约是作为send()和operatorSend()操作的一部分调用的,因此只能由持币者调用。一般而言,代币运营者合约侧重于扩展代币合约的功能。代币控制合约则侧重于控制来自账户的代币流。下表汇总了代币运营者合约和代币控制合约之间的不同点:
与ERC-20的兼容性
敏锐的读者可能已经注意到了,虽然ERC-20和ERC-777提供的功能差不多,但是二者对这些功能的命名都不尽相同;ERC-20使用的名称是transfer()/approve()/transferFrom(),而ERC-777使用的名称是send()/operatorSend()。这就意味着,同一个代币合约有可能提供相同的ERC-20和ERC-777功能。ERC-777标准中详细说明了具体的操作方式和触发事件。ERC-777实现
ERC-777带有一个参考实现,其中还包括了一个可兼容ERC-20标准的版本。代币运营者合约和代币控制合约的样本可单独获得。更多关于ERC-777的信息
这里还有一篇文章也深入剖析了ERC-777代币运营者合约,里面提到了很多例子,都是关于如何使用该合约来扩展基本的ERC-777代币合约的功能的。
郑重声明: 本文版权归原作者所有, 转载文章仅为传播更多信息之目的, 如作者信息标记有误, 请第一时间联系我们修改或删除, 多谢。