目前很多项目都使用 Tokens 来充当项目的代币,其实 Tokens 的本质就是合约里的一个变量,而 Tokens 那么火热的原因之一便是有相应的标准,这个标准便是本文要讨论了 ERC-20 标准。
ERC-20 是以太坊上的一个代币协议,我们可以将其理解成一组接口,基于 ERC-20 协议开发的代币便认为是标准化代币,标准化的好处便是兼容性,大家都按这个标准来玩,生态发展也会很好,比如各种以太坊钱包对 ERC-20 代币的支持,以及各种中心化或去中心化交易所对 Tokens 代币的支持。
USDT 是 ERC-20 代币里使用场景非常广泛的稳定币,所谓稳定币,即 1USDT=1 美元,不会有太大的价格波动,背后是美元背书(那美元背后是啥呢?以前是黄金,现在是石油和军事力量)。
本文,我们会先简单介绍 ERC-20 代币,然后再逐行分析 USDT 的合约代码(USDT 在不同链上都有相应的实现,本文讨论的是以太坊链上的 USDT)。
ERC-20 协议
ERC-20 是 ETH 下的用于实现代币的协议,协议的所有细节可以看:https://eips.ethereum.org/EIPS/eip-20
BSC 上有名为 BEP-20 的协议,它是 Bianca 参考 ERC-20 弄出来的具有相似功能的协议,因为定义的接口函数相同,MetaMask 同样可以连上,导致有些朋友将币转错链,从而导致代币的遗失。
通过 eip-20 可知,ERC-20 的标准接口如下:
contract ERC20 {
function name() constant returns (string name)
function symbol() constant returns (string symbol)
function decimals() constant returns (uint8 decimals)
function totalSupply() constant returns (uint totalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns (bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint remaining);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
逐个解释一下上述的接口函数:
name (): 返回 string 类型的 ERC-20 代币的名称
symbol (): 返回 string 类型的 ERC-20 代币符号,可以理解为代币的简称
decimals (): 当前代币支持几位小数,如果为 3,则支持 0.001 粒度的代币。在智能合约里,其实是不存在小数的,而是通过 unit256 来存在,deciaml 用于表示 uint256 存储的值中有几位代表小数。
totalSupply (): 代币总发行量
balanceOf (address _owner): _owner 地址中代币的余额
transfer (address _to, uint _value): 转账函数,假设地址 A 调用了 transfer 函数,则表示将地址 A 中_value 个 token 转给地址_to
approve (address _spender, uint _value): 授权函数,允许_spender 地址从自己的账户地址中转移_value 个 token(很多钓鱼网站使用的函数)
transferFrom (address _from, address _to, uint _value): 转账函数,与 approve 函数搭配使用,通过 approve 函数获得_from 地址的授权,授权对象是当前调用 transferFrom 函数的合约地址,此时合约便可以转移_from 地址中的_value 个 token 到_to 地址中
allowance (address _owner, address _spender): 返回_spender 地址还能从_owner 地址中提取多少 token,当合约通过 approve 函数获得某地址授权后,默认是可以转移该地址中所有的 token 的。
event Transfer (address indexed _from, address indexed _to, uint _value): 转账事件,所谓事件,其本质是记录的日志,当转账事件发生时,外界是无法监听相关数据的,即不知道转账发生了,此时的解决方法便是利用事件,将信息记录起来,外界通过监听事件来判断合约做了什么,因为事件本身的作用与目的,发送事件也被称为广播事件。
event Approval (address indexed _owner, address indexed _spender, uint _value): 授权事件
ERC-20 标准中,transfer、approve、transferFrom 这三者是比较容易搞混的函数,这里简单解释一下:
transfer (address _to, uint _value) 是单纯的转账,假设账户 A 调用了 transfer 函数给账户 B 转账,形式为:transfer (B, 100),该函数的会检查 A 地址中是否有 100 个相关代币,如果有,则将 100 个代币转到 B 地址中。
transferFrom (address _from, address _to, uint _value) 是替别人转账,假设账户 A 中有 100 个代币,账户 A 同样想转给账户 B,但他不想直接转,此时便出现了账户 C(通常是一个合约),合约 C 会通过 approve 函数获得操作账户 A 中代币的授权,然后合约 C 通过 transferFrom 函数将账户 A 中的 100 个代币转移给账户 B。
你可能会疑惑,转账为啥要还要引入合约 C 这个第三方?直接用 transfer 函数转账不行吗?
只使用 tranfer 函数进行转账是没有问题的,但并不能满足所有的情况,比如 DeFi 的各种借贷应用,本质还是将账户 A 的代币转给账户 B,但账户 A 之所以会借是因为有借出去的代币可以产生利息收益,这个功能就需要一个独立于账户 A 与账户 B 之外的合约 C 来实现,比如 Uniswap 就会获取你账户中代币的操作权限。