TokenMintERC20Token

本文主要介绍 SHIBA INU 合约
该合约是一个简单的 ERC20 代币合约。
目前,本文包括两部分:

  • 第一部分是 SafeMath 库 ,包含了基础算术运算。
  • 第二部分,是合约中出现的主要函数。

SafeMath

SafeMath 是一个在合约中定义的库。在 SHIB 合约中,它被实现为对基本算术:加减乘除的安全操作。并且,这些操作将被应用在所有 uint256 变量上。

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
26
27
28
29
30
31
32
33
34
library SafeMath {

function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}

function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}

function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}

function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
return c;
}

function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}

上述代码可知,一旦不满足计算需求(require),将抛出异常,终止合约执行。

根据 官方文档,库和合约类似,但是它的目的,被定位为一次部署,其它代码复用(使用 EVM 的消息调用特性完成调用)。这就意味着,库中函数的调用,是执行在调用合约的上下文环境中,也因此,其中的 this 是指向调用合约(calling contract)。
库中的函数,应是无状态的(stateless),因此,即为 view 或者 pure,它不修改状态。
同时,它还具有以下特性:

  • 无法被继承也无法继承
  • 不能发送 eth
  • 无法被销毁

函数

totalSupply() -> uint256

现存的 token 总量。这个函数是一个 getter 方法,因此不会修改合约状态。需要注意的是,在 Solidity 中没有浮点(早期版本),因此,对于一个采用了 18位小数精度的 token,返回值 1000000000000000000 代表 1 个 token。
如果合约允许,total supply 是可变的。比如,你可以挖出新的 token,这时,total supply 就是增加的,如果你销毁了部分 token,此时 total supply 将减少相应的数额。
一种另类的销毁 token 的方式,是将 token 发送到一个不知道私钥的账户地址,比如 0 地址,这种方式和销毁有着相同的功效,但是,其并不会使 token 的总量变少。

balanceOf(address account) → uint256

获取指定账户所拥有的 token 量。只要有 address,都可以查询该地址所拥有的 token,所有数据都公开在区块链上。

1
2
3
4
5
mapping(address => uint256) private _balances;

function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}

transfer(address recipient, uint256 amount) → bool

从调用者处转移指定 amount 的 token 给 recipient。如果转账成功,将返回 true。需要注意的是,该方法不会检查接收地址的有效性。换句话说,任何 40 个十六进制字符,都是合法地址。转账时,也不会验证接收方是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}

function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");

_balances[sender] = _balances[sender].sub(amount); // sender 余额减少 amount
_balances[recipient] = _balances[recipient].add(amount);// recipient 余额增加 amount
emit Transfer(sender, recipient, amount);
}

approve(address spender, uint256 amount) → bool

调用者(委托方)给 spender 授权 amount 数量的 token(限额),使得 spender 作为受托方可以从委托方账户中完成最多 amount 数量的交易给其它第三方。

1
2
3
4
5
6
7
8
9
10
11
12
13
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}


function _approve(address owner, address spender, uint256 value) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");

_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}

从上述关系中,我们可以知道这种授权关系,其实是记录在 _allowances map 中,其建立了所有从 ownerspender 的授权额度。而且,这个额度也是可以调整的,下面将进行介绍。

allowance(address owner, address spender) → uint256

查询当前 owner 授权给 sender token 的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 查询某个地址的授权额度
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}

// 增加对 spender 的授权额度
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}

// 减少对 spender 的授权额度
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}

transferFrom(address sender, address recipient, uint256 amount) → bool

approve 调用完成了委托方向受托方的 token 定量授权。此后,受托方调用合约中的 transferFrom 方法,帮助委托方完成交易。

1
2
3
4
5
6
7
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
// 从 sender 转账 amount 给 recipient,当前,前提是 sender 有超过 amount 数量的 token
_transfer(sender, recipient, amount);
// 减少 msg.sender 的授权额度,如果授权额度在此之前已经用完,整个交易会回滚
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
return true;
}

approve/transferFrom 机制,主要解决 三方 之间的转账问题。有时,发送者希望授权第二方能够代表他完成有限数量 token 的转移。

举个例子:

  • 调用 X合约(我随便定义的一个合约,以和 Uniswap合约 对比)中 approve 方法将允许 Uniswap合约 在自己账户中转移出 N 个 token
  • 此时,可以调用 Uniswap 合约 中的 兑换函数,兑换一些 ETH。该函数将自动调用 X合约 中的 transferFrom 函数,实现从你的账户中转移出 N 个 token ,并将对应的 ETH 转入到你的账户。所有上述过程,将发生在同一个交易中。

_burn(address account, uint256 amount) internal

该方法也不属于标准的 ERC20 协议。账户销毁指定 amount 的 token。

1
2
3
4
5
6
7
8
function _burn(address account, uint256 value) internal {
require(account != address(0), "ERC20: burn from the zero address");
// 供应总量减少
_totalSupply = _totalSupply.sub(value);
// 账户 token 减少
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}

_burnFrom(address account, uint256 amount) internal

减少 sender 的授权额度,并将这个额度销毁。其实是 _burn_approve 的结合,二合一,减少外部调用次数。

1
2
3
4
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, msg.sender, _allowances[account][msg.sender].sub(amount));
}

burn(uint256 value) public

外部真正暴露的 burn 函数。

1
2
3
function burn(uint256 value) public {
_burn(msg.sender, value);
}

_mint(address account, uint256 amount) internal

_burn 相对立的函数。同样的,其不仅仅给某个账户新增加了 amount 的token,同时还将增量同步到了 _totalSupply

1
2
3
4
5
6
7
8
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
// 供应总量增加
_totalSupply = _totalSupply.add(amount);
// 账户 token 增加
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}

constructor

1
2
3
4
5
6
7
8
9
10
11
12
constructor(string memory name, string memory symbol, uint8 decimals, uint256 totalSupply, address payable feeReceiver, address tokenOwnerAddress) public payable {
_name = name;
_symbol = symbol;
_decimals = decimals;

// set tokenOwnerAddress as owner of all tokens
// 将总供应量转移到指定地址 tokenOwnerAddress
_mint(tokenOwnerAddress, totalSupply);

// pay the service fee for contract deployment
feeReceiver.transfer(msg.value);
}

参考链接

Solidity Tutorial: all about Libraries

UNDERSTAND THE ERC-20 TOKEN SMART CONTRACT

understanding-erc-20-token-contracts

Sending a transaction to a non-existent address

What is the use case of transferFrom function in ERC20 token contract?

Send tokens using approve and transferFrom vs only transfer

ERC20.sol

© 2024 YueGS