就像做 pwn 要读 glibc 源码,做 blockchain 怎么能不分析 openzeppelin 呢
[1] ERC20
ERC20 是以太坊上最基本的代币规范
openzeppelin
ERC20 源码在这
以下是该合约的变量
1 2 3 4 5 6
| mapping(address account => uint256) private _balances; // account 该 token 的余额 // account 批准 spender 使用的余额,实际上花的还是 account 的 tokens mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; // 该合约总共发行的 token 数 string private _name; // 该 token 的 name string private _symbol; // 简约版的 token name,比如
|
下面是一些比较易懂的函数接口(基本都是 getter 函数):
1 2 3 4 5 6 7 8
| constructor(string memory name_, string memory symbol_); // ERC20("HeyGapToken", "HGT") function name() public view virtual returns (string memory); // 返回 HeyGapToken function symbol() public view virtual returns (string memory);// 返回 HGT function decimals() public view virtual returns (uint8); // 返回 18, 就是 1e18 的那个 18 function totalSupply() public view virtual returns (uint256) // 返回 totalsupply function balanceOf(address account) public view virtual returns (uint256) // 返回账户余额 // 返回 owner 批准 spender 的开销 _allowances[owner][spender] function allowance(address owner, address spender) public view virtual returns (uint256);
|
update 相关函数:该函数用于更新 _totalSupply, _balances
这两个变量,值得注意的是这些变量都不会溢出,因为 value <= fromBalance
<= totalSupply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { _balances[from] = fromBalance - value; } }
if (to == address(0)) { unchecked { _totalSupply -= value; } } else { unchecked { _balances[to] += value; } }
emit Transfer(from, to, value); }
|
transfer 相关函数:将 msg.sender 的钱转移给 to,会检测 msg.sender 和
to 是不是 address(0),如果是的话就 revert
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; }
function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); }
|
approve 相关函数:增加 msg.sender 的 spender 余额,其中
_approve(address, address, uint, bool) 这个函数是可以由用户重写的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; }
function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); }
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } }
|
transferFrom相关函数:approve 函数 和 transfer 函数的结合。假设 A
通过 approve 函数批准 B 花销 A 的 x 个 token,那么 B 就可以调用
transferFrom 函数来花费不超过 x 个 token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; }
function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance < type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } }
|
[1] ERC721
ERC721 是以太坊上“同质化代币”的规范