深入理解以太坊上代币标准的设计,有助于确保正确的进行智能合约开发并确保安全,这也是本课程的主要目标。以下是以太坊上最受欢迎的一些代币标准:
- ERC20 :同质化(可互换)代币的标准接口,比如投票代币、质押代币或虚拟货币。
- ERC721 :非同质化代币的标准接口,比如艺术作品或歌曲的契约。
- ERC777 :ERC777 使人们能够通过代币上创建额外的功能,例如用于改善交易私密性的混合合约,或是在您不慎丢失私钥时的紧急恢复功能。
- ERC1155 :ERC1155 允许更有效的交易和打包交易,从而节省燃料成本。 此代币标准允许创建实用代币(例如 BNB 或 BAT)和加密朋克之类的非同质化代币。
一、ERC20 协议
ERC20 提供了一个同质化代币(Fungible Token, FT)的标准,换句话说,每个代币与另一个代币(在类型和价值上)完全相同。例如,一个 ERC20 代币就像以太币一样,意味着一个代币会并永远会与其他代币一样。
ERC20 是由 Fabian Vogelsteller 于 2015 年 11 月在 EIP20 提案中提出的。这是一个能实现智能合约中代币的应用程序接口标准。
EIP20 代币标准
EIP20 提案是由 Fabian Vogelsteller 和 Vitalik Buterin 在 2015 年 11 月创建的代币标准,定义了代币合约的标准接口,提供了转移代币的基本功能,并允许代币获得批准,以方便其他链上的第三方使用。此外,该标准接口允许以太坊上定义的任何代币被其他应用程序(钱包、去中心化交易所等)重用。
ERC20 功能
ERC20 的功能包括但不限于以下几点:
- 转账:将代币从一个帐户转到另一个帐户
- 查询余额:获取帐户的当前代币余额
- 查询总量:获取网络上可用代币的总供应量
- 代币授权:批准一个帐户中一定的代币金额由第三方帐户使用
函数

注意:
(1)合约使用的 Solidity 语言版本必须是 0.4.17 或以上;
(2)对于返回值 success,调用者必须处理 success 为 false 的情况,不能假设 success 永远为 true 而忽略 success 为 false 的情况。
1. name

返回代币合约中定义的代币名称,比如”MyToken”。
该函数读取代币合约中定义的代币名称。这里,定义代币名称可以提高代币合约的可用性,但不是代币合约必须定义的属性,但标准的 ERC20 合约都会定义。
2. symbol

返回代币合约中定义的代币符号,比如”HIX”。
该函数读取代币合约中定义的代币符号。这里,定义代币符号可以提高代币合约的可用性,但不是代币合约必须定义的属性,但标准的 ERC20 合约都会定义。
3. decimals

返回代币合约中定义的代币的精度,比如 18,表示代币的余额除以 10^18 后才是真正的代币数量。类似于以太的单位换算,余额 10^18 Wei 表示以太数量为 1=10^18/10^18。
该函数读取代币合约中定义的代币精度。这里,定义代币精度可以提高代币合约的可用性,但不是代币合约必须定义的属性,但标准的 ERC20 合约都会定义。
4. totalSupply

返回代币的总供应量。
5. balanceOf

返回地址为_owner 的账户的代币余额。
6. transfer

将调用者账户中_value 数量的代币转移到地址_to,并且必须触发 Transfer 事件。如果调用者的账户余额没有足够的代币可以支付_value 数量的代币时,该函数应该抛出异常并回滚。
注意:0 值作为_value 值传入到函数必须被视为正常的函数执行并触发 Transfer 事件。
7. transferFrom

将_value 数量的代币从地址_from 转移到地址_to,并且必须触发 Transfer 事件。
transferFrom 方法用于提款流程,允许合约代表代币所有者账户转移代币。 例如,这可用于允许合约代表代币所有者转移代币和 / 或以次级货币(sub-currency)的形式收取费用。若函数调用者没有得到_from 账户的授权,该函数则会抛出异常并回滚。
注意:0 值作为_value 值传入到函数必须被视为正常的函数执行并触发 Transfer 事件。
8. approve

该函数的调用者账户地址将_value 数量的代币授权给_spender,或者更新授权的代币数量,即_spender 可以多次提取调用者账户地址的代币,总数量不超过_value,每次提取的代币数量也不能超过调用者账户地址的代币余额,同时提取后会更新_value 的值。如果在提取过程中,提取的代币数量超过了代币余额不足,该函数会抛出异常并回滚。
9. allowance

返回账户地址_spender 还可以从账户地址_owner 中提取的代币数量。
事件

1. Transfer

Transfer 事件必须在代币转移时触发,包括转移的代币数量为 0 的情况。
代币合约在铸造代币时应该触发 Transfer 事件,并将_from 地址设置为 0。此时,也可以自定义并触发 Mint 事件:

代币合约在销毁代币时应该触发 Transfer 事件,并将_to 地址设置为 0x。此时,也可以自定义并触发 Burn 事件:

2. Approval

必须在任何调用 approve 函数并执行成功时触发。
二、ERC721 协议
ERC721 为 NFT 引入了一个标准,换言之, 这种类型的代币是独一无二的,并且可能与来自同一智能合约的另一代币有不同的价值,也许是因为它的年份、稀有性、甚至是它的观感。
每一个 NFT 都有一个 uint256 类型的变量,名为 tokenId,所以对于任何 ERC721 合约,这对值 <contract address, tokenId> 必须是全局唯一的。dApp 可以有一个 “转换器”,该转换器输入 tokenId,然后输出一些非常有趣的事物的图像,比如僵尸、武器、技能或非常可爱的猫咪等。
ERC721 是由 William Entriken、Dieter Shirley、Jacob Evans、Nastassia Sachs 于 2018 年 1 月在 EIP721 中提出的一个在智能合约中实现代币 API 的非同质化代币标准。
BIP721 代币标准
BIP721 提案描述了智能合约中实现 NFT 的标准 API。 标准提供了跟踪和转移 NFT 的基本功能。该提案考虑了个人拥有和交易 NFT 的场景,以及委托给第三方(如:代理人 / 钱包 / 拍卖人,称为 “操作员”)的情况。
NFT 可以代表对数字或物理资产的所有权。我们考虑了资产的多样性,并且我们知道您会想要代表更多:
- 实体财产,如:房屋,独特的艺术品;
- 虚拟收藏品,如:小猫,收藏卡的独特图片;
- “负” 资产,如:贷款等。
总的来说,所有房屋都是不同的,没有两只小猫是一样的。NFT 是可区分的,必须分别跟踪每个所有者。
ERC721 功能
ERC721 的功能包括但不限于以下几点:
- 转账:将代币从一个帐户转移到另一个帐户;
- 查询余额:获取帐户的当前代币余额;
- 查询所有者:获取代币的所有者;
- 查询总量:获取整个网络的可用代币总供应量;
- 代币授权:批准帐户中一定数量的代币可以被第三方帐户转移。
函数

以上定义的函数都是外部函数,函数只能进行外部调用。
1. balanceOf

查询余额,返回地址_owner 拥有的合约定义的 NFT 的数量,若没有,则返回 0。
2. ownerOf

查询所有者地址,返回 tokenId 为参数_tokenId 的 NFT 的所有者地址。
3. safeTransferFrom

安全地转移 NFT 所有权并发送数据,即将 tokenId 为参数_tokenId 的 NFT 的所有权由地址_from 转移给地址_to,并将额外的数据 data 发送给地址_to,并触发 Transfer 事件。参数 data 没有格式要求。若地址_to 是合约地址,data 一般包含函数签名值(methodId)以及函数参数值,当 data 发送给_to 时,会调用合约_to 中签名值对应的函数或者回调函数。
函数能够执行,必需满足以下条件:
- 参数_from 是_tokenId 对应的 NFT 的所有者
- 调用者 sender 有权转移_tokenId 对应 NFT 的所有权,即:msg.sender 是_tokenId 对应 NFT 的所有者或者得到了其授权。
- 参数_to 不是零地址
若不满足条件,函数将抛出异常并回滚。
另外,该函数使用了 payable 修饰符,因此可以接收以太坊原生代币(以太币)。
4. safeTransferFrom

安全地转移 NFT 所有权,即将 tokenId 为参数_tokenId 的 NFT 的所有权由地址_from 转移给地址_to,并触发 Transfer 事件。
调用该函数等价于调用上面的 safeTransferFrom 中 data 参数是”” 的情况,即:

5. transferFrom

转移 NFT 所有权,即将 tokenId 为参数_tokenId 的 NFT 的所有权由地址_from 转移给地址_to,并触发 Transfer 事件。函数能够执行,必需满足以下条件:
- 参数_from 是_tokenId 对应的 NFT 的所有者
- 调用者 sender 有权转移_tokenId 对应 NFT 的所有权,即:msg.sender 是_tokenId 对应 NFT 的所有者或者得到了其授权。
- 参数_to 不是零地址
若不满足条件,函数将抛出异常并回滚。
6. approve

将 tokenId 为参数_tokenId 的 NFT 授权给地址_approved,或者更新授权地址为_approved,并且触发 Approve 事件。函数能够执行,必需满足以下条件:
- 调用者 sender 有权转移_tokenId 对应 NFT 的所有权,即:msg.sender 是_tokenId 对应 NFT 的所有者或者得到了其授权。
若不满足条件,函数将抛出异常并回滚。
7. setApprovalForAll

将调用者 msg.sender 的所有 NFT 资产授权给_operator 地址来管理,或者取消授权,并且触发 ApprovalForAll 事件。
参数_approved 是 true 代表授权;参数_approved 是 false 代表取消授权。
8. getApproved

查询单个 NFT 的授权地址。若_tokenId 不是一个有效的 NFT 的 tokenId,该函数会抛出异常并回滚。
9. isApprovedForAll

查询地址_owner 的所有 NFT 是否授权给了另一个地址_operator 来管理。若已授权,则返回 true;否则,返回 false。
事件

1. Transfer

该事件在 NFT 转移所有权完成时触发。
代币合约在铸造 NFT 时应该触发 Transfer 事件,并将_from 地址设置为 0。此时,也可以自定义并触发 Mint 事件:

代币合约在销毁 NFT 时应该触发 Transfer 事件,并将_to 地址设置为 0x。此时,也可以自定义并触发 Burn 事件:

2. Approval
当将_owner 地址的单个 NFT(其 tokenId 为参数_tokenId)授权给_approved 地址时,在授权完成后触发 Approval 事件。

3. ApprovalForAll

当将_owner 地址的所有 NFT 授权给_operator 地址或者取消该授权时,触发 ApprovalForAll 事件。
三、ERC1155 协议
ERC1155 协议是由 Witek Radomski, Andrew Cooke 等于 2018 年 6 月在 EIP1155 提案中提出的用于多种代币管理的合约标准接口。单个部署的合约可以包括同质化代币、非同质化代币或其他配置(如半同质化代币)的任何组合。
多代币标准的目的是创建一个智能合约接口,可以代表和控制任何数量的同质化和非同质化代币类型。这样一来,ERC-1155 代币就具有与 ERC20 和 ERC721 代币相同的功能,甚至可以同时使用这两者的功能。而最重要的是,它能改善这两种标准的功能,使其更有效率,并纠正 ERC20 和 ERC721 标准上明显的实施错误。
BIP1155
BIP1155 提案概述了一个标准的智能合约接口,可以代表任意数量的同质化代币和非同质化代币类型。ERC20 等现有标准要求为每种代币类型部署单独的合约。ERC721 标准的代币 ID 是一个单一的非同质化索引,每组非同质化代币都会被部署为具有统一设置的独立的合约。相比之下,ERC1155 多代币标准允许每个代币 ID 表示一个新的可配置代币类型,它可以有自己的元数据、供应量以及其他属性。
每个函数的参数集合中都会包含_id 参数,表示交易中的一种特定的代币或代币类型。
ERC1155 功能
ERC1155 的功能包括但不限于以下几点:
- 批量传输:通过一次合约调用传输多种资产;
- 批量余额:在一次调用中获取多个资产的余额;
- 批量授权:授权同一地址的所有代币;
- Hook:接收代币的钩子函数;
- 支持非同质化代币:如果供应量仅为 1,将其作为非同质化代币(NFT)处理;
- 安全转账规则:安全转账规则集。重要规则如下:
- 调用者必须获得批准才能从 _from 的账户地址消费代币,或者调用者账户地址必须与 _from 的账户地址相同。
- 在以下情况下,转账调用将回滚:
(1)_to 地址为 0;
(2)_ids 的长度与_values 的长度不同;
(3)_ids 中代币持有者的任何余额低于发送给接收者的相应_value 金额;
(4)出现任何其他错误。
函数
批量传输

- transferFrom!

ERC20 代币转移,从地址 from 将 value 数量的代币转移到地址 to,与 ERC20 协议中的 transferFrom 功能相同。
- safeBatchTransferFrom!

ERC1155 代币批量转移,包括同质化代币以及非同质化代币。与 ERC20 与 ERC721 相比,ERC1155 中唯一的区别是将数值作为数组参数进行传递,此外还会传递数组 id。例如,给出 ids=[3, 6, 13]和 values=[100, 200, 5],传输结果如下:
- 将 id=3 的 100 个代币从地址_from 传输到地址_to;
- 将 id=6 的 200 个代币从地址_from 传输到地址_to;
- 将 id=13 的 5 个代币从地址_from 转移到地址_to。
ERC1155 中只有 transferFrom,没有 transfer。要想像常规的 transfer 一样使用它,只需将 "from" 地址设为调用该函数的地址。
批量余额

- balanceOf

查询 owner 地址的余额,与 ERC20 协议中的 balanceOf 功能相同。
- balanceOfBatch

账户余额批量查询,在单次调用中获取多个余额。参数中传递所有者账户数组和代币的 id 数组。例如,对于给出的_ids=[3, 6, 13]和_owners=[0xbeef..., 0x1337..., 0x1111...],返回值将为:

批量授权

- setApprovalForAll

设置操作帐户_operator 为已授权账户或取消授权。授权过程与 ERC20 略有不同。这里不是授权特定金额,而是类似于 ERC721 协议中的授权,通过 setApprovalForAll 函数,对操作账户进行完全的授权或取消授权。
- isApprovedForAll

查询当前的审批状态,即_owner 是否对_operator 进行了授权。结果为 true 代表全部授权;结果为 false 代表未授权。 不能定义要授权代币的数量,甚至代币类型。
接收钩子

ERC1155 只支持智能合约的接收钩子函数。钩子函数必须返回一个事先预定义的 4 字节值,这个值被指定为:

当接收合约返回这一值时,意味着合约知道如何处理 ERC1155 代币并接受转账。
事件

1. TransferSingle

TransferSingle 事件在代币转移成功时触发,包括转移的代币数量为 0 的情况。另外,代币铸造和销毁时也可以触发 TransferSingle 事件。与 ERC20 协议中的 Transfer 事件相同。
2. TransferBatch

TransferBatch 事件在代币批量转移成功时触发,包括转移的代币数量为 0 的情况。另外,代币批量铸造和销毁时也需要触发 TransferBatch 事件。
3. ApprovalForAll

当将_owner 地址的所有代币全部授权给_operator 地址或者取消该授权时,触发 ApprovalForAll 事件。
4. URI

当一个代币 ID 的 URI 更新成功时触发 URI 事件。
四、其他相关代币协议
ERC777 协议
ERC777 协议是一个易于交易的代币标准,可改进现有的 ERC20 标准。与 ERC20 相比,ERC777 提供了以下改进:
- 钩子
钩子是智能合约代码中描述的一个函数。钩子将会在代币通过合约发送或者接收时调用。这将允许智能合约对进出的代币做出互动。
- 小数位数
该标准还解决了在 ERC20 中造成的 decimals 的混乱。这种清晰度提升了开发者体验。
- 向后兼容 ERC20
ERC777 合约可以与 ERC20 合约进行互动。
更多内容,请见EIP777。
ERC165 协议
ERC165 协议创建了一个标准方法来发布和检测智能合约实现的接口。更多内容详见EIP165。