就像做 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 是以太坊上“同质化代币”的规范