前言
前面对Solidity的语法都进行了一次了解,现在来系统学习一下合约开发最佳实践,先学习下什么是ERC20,经常听到这个协议
首先写个最简单合约demo熟悉Solidity语法先
“Hello World”
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
| // SPDX-License-Identifier: GPL-3.0 # 默认写上即可,这是开源声明
pragma solidity ^0.8.24; # 当前合约solidity的版本
error OnlyOwner(address caller); # 错误事件定义,更够节省Gas
error ZeroValue(); # 错误事件
contract SyntaxPrimer { address public owner; # 地址类型 string public appName; uint256 public counter; bool public paused;
mapping(address=>uint256) private scores; # 映射 就是map
event ScoreUpdated(address indexed user,uint256 score); # 事件定义,记录每个人score更改记录
modifier onlyOwner(){ # 权限修饰函数 if (msg.sender!=owner) { revert OnlyOwner(msg.sender); } _; }
constructor(string memory initName){ # 构造 owner = msg.sender; appName = initName; }
function setScore(address user,uint256 score) external onlyOwner{ # 设置分数 scores[user] = score; emit ScoreUpdated(user, score); }
function getScore(address user) external view returns (uint256){ # 查看分数 return scores[user]; }
}
|
上面是一个demo合约,写完之后可以直接丢到Redmix里面编译执行,可以看到有个Compliy的按钮,点击后,即可在左边表单里进行Deploy,然后就可以通过Deployed Contracts里调用函数了,这就是一个合约的学习过程。

ERC20
简介
ERC20可以理解为:”以太坊上同质化代币的通用接口标准“,就好像我们平常开发约定的共识,只要实现了ERC20标准指定的函数和事件,很多钱包、交易所、DEX、DeFi协议就能直接进行交互了。
一句话来说就是:统一交互接口
同质化:fungible token,每一单位的token都完全等价
比如你手里的1USDT == 我手里的1USDT
所以ERC20协议适合:
- 稳定币
- 治理代币
- 平台币
- 游戏金币
- 质押凭证
- LP token
数据模型
ERC20最核心是两张表,余额表和授权表
1 2 3 4 5 6 7 8 9 10 11
| mapping(address => uint256) private _balances; 余额表,记录了每个地址有多少代币
address A -> 1000 address B -> 500 address C -> 0
mapping(address => mapping(address => uint256)) private _allowances; # 授权表,owner 允许 spender 使用多少 token
Alice 授权 Bob 最多花 300 个 token allowances[Alice][Bob] = 300
|
标准核心函数有6个
1 2 3 4 5 6
| function totalSupply() external view returns (uint256); # 总供应量 function balanceOf(address account) external view returns (uint256); # 查询某个地址有多少token function transfer(address to, uint256 value) external returns (bool); # 转账函数,调用方转出 function allowance(address owner, address spender) external view returns (uint256); # 查询owner授权spender可以花多少额度 function approve(address spender, uint256 value) external returns (bool); # 批准spender地址可以代表你使用多少token function transferFrom(address from, address to, uint256 value) external returns (bool); # 授权的转账,如果 Alice 已经授权 Bob 300 个 token,那么 Bob 可以调用:transferFrom(Alice, Carol, 100)
|
这里第一眼比较奇怪就是:为什么需要 approve + transferFrom,主要是因为在DeFi场景经常是需要某个合约帮你操作,例如当我们要在Uniswap里换币时,通常会弹出一个Approve,然后在弹一次Swap
1 2 3 4 5 6 7 8
| 我有 USDT 我想换成 ETH Uniswap Router 需要从我的账户里拿走 USDT
1. 我调用 USDT.approve(UniswapRouter, 1000) 2. UniswapRouter 调用 USDT.transferFrom(我, 池子, 1000) 3. Uniswap 再把 ETH 给我
|
mint 和 burn
在ERC20标准里没有规定一定要有 mint 和 burn,但真实项目通常都会有
mint: 增发,总供应增加,通常就是owner才会操作
burn:销毁,总供应减少
ERC20里最核心的就是转账和授权,不过实际上还会引入一个扩展协议 meta
1 2 3
| function name() external view returns (string memory); # token是什么 function symbol() external view returns (string memory); # 符号是什么 function decimals() external view returns (uint8); # 显示小数点应该怎么算
|
decimals比较重要点,首先链上 ERC20 不存小数,只存整数,decimals函数负责告诉钱包和前端怎么把整数显示成人类可读数量。
在Solidity/EVM里没有真正的小数类型,因此就好像我们做支付系统时,有时候在数据里是直接存分单位的
因此在ERC20合约里,当decimals返回18,代表
1 2 3 4
| 1 个完整 token = 10^18 个最小单位 1 token = 1000000000000000000 2.5token = 2500000000000000000 10000000000000000 = 0.01 token
|