0%

ERC-20

什么是 ERC-20?

ERC-20 提出了一个同质化代币的标准,换句话说,它们具有一种属性,使得每个代币都与另一个代币(在类型和价值上)完全相同。 例如,一个 ERC-20 代币就像以太币一样,意味着一个代币会并永远会与其他代币一样。(From: 《ERC-20 代币标准》)

方法(Method)

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
function name() public view returns (string)
// 返回令牌的名称 - 例如 "MyToken"
// 可选 - 此方法可用于提高可用性,但接口和其他协定不得期望存在这些值。

function symbol() public view returns (string)
// 返回令牌的 symbol。例如“HIX”。
// 可选 - 此方法可用于提高可用性,但接口和其他协定不得期望存在这些值。

function decimals() public view returns (uint8)
// 返回代币使用的小数位数 - 例如 8,表示将代币数量除以 100000000 以获得其用户表示。
// 可选 - 此方法可用于提高可用性,但接口和其他协定不得期望存在这些值。

function totalSupply() public view returns (uint256)
// 返回总代币供应量。

function balanceOf(address _owner) public view returns (uint256 balance)
// 返回地址为 _owner 的另一个账户的账户余额。

function transfer(address _to, uint256 _value) public returns (bool success)
// 将 _value 数量的代币转移到 _to,并且必须触发 Transfer 事件。如果消息调用者的账户余额没有足够的代币可供花费,则函数应该throw。
// 注意值为 0 的传输必须被视为正常传输,并触发 Transfer 事件。

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
// 将 _value数量的代币从地址 _from 转移到地址 _to,并且必须触发 Transfer 事件。
// transferFrom 方法用于提现工作流程,允许合约代表您转移代币。例如,这可用于允许合约代表您转移代币和/或以子货币收取费用。除非 _from 账户通过某种机制故意授权了消息的发送者,否则该函数应该throw。

function approve(address _spender, uint256 _value) public returns (bool success)
// 允许_spender多次从您的账户提款,最高可达_value金额。如果再次调用此函数,它将用 _value 覆盖当前限额。
// 注意:客户端应该确保在创建用户界面时,先将限额设置为 0,然后再为同一花费者将其设置为另一个值。尽管 Contract 本身不应该强制执行它,以允许向后兼容之前部署的 Contract

function allowance(address _owner, address _spender) public view returns (uint256 remaining)
// 返回 _spender 仍允许从 _owner 中提取的金额。

事件(Event)

1
2
3
4
5
6
event Transfer(address indexed _from, address indexed _to, uint256 _value)
// 必须在代币转移时触发,包括零价值转移。
// 创建新代币的代币合约应该在创建代币时触发 Transfer 事件,并将 _from 地址设置为 0x0。

event Approval(address indexed _owner, address indexed _spender, uint256 _value)
// 必须在成功调用 approve(address _spender, uint256 _value) 时触发。

实现

简单示例

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MyToken {
string public constant name = "Bob's Token";
string public constant symbol = "BBK";
uint8 public constant decimals = 18;
uint16 private constant increase = 1000;
uint256 public constant totalLimit = 27000000 * (10 ** decimals);
uint256 public totalSupply = 0;
address private owner;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);

modifier checkAddress(address _addr) {
require(address(_addr) != address(0), "Invalid address.");
_;
}

modifier checkBalanceOf(address _addr, uint256 _value) {
require(balanceOf[_addr] >= _value, "Insufficient balance");
_;
}

modifier checkOwner() {
require(msg.sender == owner, "Not owner.");
_;
}

constructor() {
owner = msg.sender;
}

function mint(address _to) public checkOwner returns (uint256) {
uint256 mintValue = increase * (10 ** decimals);
require(totalLimit >= (totalSupply + mintValue), "Out of limit.");
totalSupply += mintValue;
balanceOf[_to] += mintValue;
return balanceOf[_to];
}

function transfer(address _to, uint256 _value) public checkAddress(_to) checkBalanceOf(msg.sender, _value) returns (bool success){
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}

function transferFrom(address _from, address _to, uint256 _value) public checkAddress(_to) checkBalanceOf(_from, _value) returns (bool success) {
require(allowance[_from][msg.sender] >= _value, "No enough approve value.");
allowance[_from][msg.sender] -= _value;
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
return true;
}

function approve(address _spender, uint256 _value) public returns (bool success){
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
}

注意事项

  • 由于本合约的编译版本为0.8.0,此版本具备整数溢出检查,所以可以不使用safeMath。

快速示例

使用OpenZeppelin快速创建ERC-20合约

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
35
36
37
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.22;

import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Pausable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

contract BobToken is ERC20, ERC20Pausable, AccessManaged, ERC20Permit {
constructor(address initialAuthority)
ERC20("BobToken", "BBK")
AccessManaged(initialAuthority)
ERC20Permit("BobToken")
{}

function pause() public restricted {
_pause();
}

function unpause() public restricted {
_unpause();
}

function mint(address to, uint256 amount) public restricted {
_mint(to, amount);
}

// The following functions are overrides required by Solidity.

function _update(address from, address to, uint256 value)
internal
override(ERC20, ERC20Pausable)
{
super._update(from, to, value);
}
}

我们的合约通常通过继承来使用,在这里我们将 ERC20 重新用于基本标准实现以及 namesymboldecimals 可选扩展。此外,我们正在创建一个代币的 initialSupply,该代币将被分配给部署合约的地址。