ERC20标准函数简介与测试方法
Posted sanqima
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ERC20标准函数简介与测试方法相关的知识,希望对你有一定的参考价值。
ERC20是以太坊上的一种合约标准,它包含5个函数、2个事件。具体如下:
- totalSupply(): token的总量
- balanceOf() :某个地址上的余额
- transfer() : 发送token
- allowance() :额度、配额、津贴
- approve() : 批准给某个地址一定数量的token(授予额度、授予津贴)
- transferFrom(): 提取approve授予的token(提取额度、提取津贴)
- Transfer() : token转移事件
- Approval() :额度批准事件
标准函数 | 含义 |
---|---|
totalSupply() | 代币总量 |
balanceOf(addresss account) | account地址上的余额 |
transfer(address recipient, uint256 amount) | 向recipient发送amount个代币 |
allowance(address owner, address spender) | 查询owner给spender的额度(总配额) |
approve(address spender, uint256 amount) | 批准给spender的额度为amount(当前配额) |
transferFrom(address sender, address recipient, uint256 amount) | recipient提取sender给自己的额度 |
Transfer(address indexed from, address indexed to, uint256 value) | 代币转移事件:从from到to转移value个代币 |
Approval(address indexed owner, address indexed spender, uint256 value) | 额度批准事件:owner给spender的额度为value |
1、ERC20标准接口
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
2、Token实现
HelloWorldToken合约,它继承IERC20接口,同时添加了name()、decimal()、increaseAllowance()、decreaseAllowance() 等函数。
扩展函数 | 名义 |
---|---|
name() | 代币名称 |
decimal() | 代币精度 |
increaseAllowance() | 增加额度 |
decreaseAllowance() | 减少额度 |
//HelloWorldToken.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
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) {
// return div(a,b,"SafeMath: division by zero");
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: modu by zero");
return a % b;
}
}
contract HelloWorldToken is IERC20 {
using SafeMath for uint256;
string private _name;
string private _symbol;
uint8 private _decimal;
uint256 private _totalSupply;
mapping (address => uint256) private _balanceOf;
mapping (address => mapping(address => uint256)) private _allowances;
constructor(string memory name,string memory symbol,uint8 decimal,uint256 initSupply) public {
_name = name;
_symbol = symbol;
_decimal = decimal;
_totalSupply = initSupply*(10**uint256(decimal));
_balanceOf[msg.sender] = _totalSupply;
}
function name() external view returns (string memory) {
return _name;
}
function symbol() external view returns (string memory) {
return _symbol;
}
function totalSupply() external override view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external override view returns (uint256) {
return _balanceOf[account];
}
function transfer(address recipient,uint256 amount) external override returns (bool) {
_transfer(msg.sender,recipient,amount);
return true;
}
function _transfer(address sender,address recipient,uint256 amount) internal {
require(sender != address(0),"ERC20: tranfer from the zero address");
require(recipient != address(0),"ERC20: tranfer to the zero address");
_balanceOf[sender] = _balanceOf[sender].sub(amount);
_balanceOf[recipient] = _balanceOf[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
function allowance(address owner, address spender) public override view returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public override returns (bool) {
_approve(msg.sender,spender,amount);
return true;
}
function _approve(address owner,address spender,uint256 amount) internal {
require(owner != address(0),"ERC20: tranfer from the zero address");
require(spender != address(0),"ERC20: tranfer to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner,spender,amount);
}
function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
_transfer(sender,recipient,amount);
_approve(sender,msg.sender,_allowances[sender][msg.sender].sub(amount));
return true;
}
function increaseAllowance(address spender,uint256 amount) public returns (bool) {
_approve(msg.sender,spender,_allowances[msg.sender][spender].add(amount));
}
function decreaseAllowance(address spender,uint256 amount) public returns (bool) {
_approve(msg.sender,spender,_allowances[msg.sender][spender].sub(amount));
}
}
3、测试方法
这里在hardhat里测试ERC20合约。
3.1 创建工程
a) 打开黑框框,依次输入如下命令
mkdir bcghat
cd bcghat
npm init -y
b) 修改package.json,主要是修改devDependencies字段
//package.json
{
"name": "bcghat",
"version": "1.0.0",
"description": "",
"main": "",
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"chai": "^4.3.4",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.4.7",
"hardhat": "^2.6.5",
"@openzeppelin/contracts": "^3.4.2",
"typescript": "^4.4.4"
}
}
c) 安装依赖包
npm install
d) 初始化工程
npx hardhat
## 在弹出的下列栏中选中"create simple project"
e)在bcghat/contracts目录,新建HelloworldToken.sol文件,把上面第2章节的代码拷贝到该文件即可。
touch HelloworldToken.sol
3.2 编写测试脚本
在bcghat/test目录,新建一个文件夹名称为erc20,然后在erc20里,新建一个文件名称为1.HWToken.js
cd bcghat
mkdir -p test/erc20
cd test/erc20
touch 1.HWToken.js
测试脚本:1.HWToken.js 内容如下
// 1.HWToken.js
// We import Chai to use its asserting functions here.
const { BigNumber } = require("@ethersproject/bignumber");
const { expect } = require("chai");
describe("Token contract", function () {
let Factory;
let hardhatToken;
let owner;
let addr1,addr2,addr3;
let addrs;
before(async function () {
// Get the ContractFactory and Signers here.
Factory = await ethers.getContractFactory("HelloWorldToken",{from:owner});
[owner, addr1, addr2, addr3, ...addrs] = await ethers.getSigners();
// Token名称:HWToken
// Token符号: HWT
// Token精度: 18
// Token总量: 10000枚
hardhatToken = await Factory.deploy("HWToken","HWT","18",10000);
console.log('owner:',owner.address);
console.log('token:',hardhatToken.address);
});
// You can nest describe calls to create subsections.
describe("Deployment", function () {
it("Check name", async () => {
expect(await hardhatToken.name()).to.equal("HWToken");
});
it("Check symbol", async () => {
expect(await hardhatToken.symbol()).to.equal("HWT");
});
it("Check totalSupply", async () => {
const totalSupply = await hardhatToken.balanceOf(owner.address);
expect(await hardhatToken.totalSupply()).to.equal(totalSupply);
});
it("Check transfer", async () => {
await hardhatToken.transfer(addr1.address,100);
expect(await hardhatToken.balanceOf(addr1.address)).to.equal(100)
})
it("check the event of transfer",async () => {
await expect(hardhatToken.transfer(addr1.address, 100))
.to.emit(hardhatToken,'Transfer')
.withArgs(owner.address,addr1.address,100);
});
it("check approve",async () => {
//addr2获取200配额
await hardhatToken.approve(addr2.address,200);
expect(await hardhatToken.allowance(owner.address,addr2.address)).to.equal(200);
})
it("check increaseAllowance", async () => {
//owner继续给addr2追加300配额,addr2现在有200+300=500配额
await hardhatToken.increaseAllowance(addr2.address,300);
expect(await hardhatToken.allowance(owner.address,addr2.address)).to.equal(500);
})
it("check transferFrom", async () => {
//addr2提取属owner给自己的50个配额,剩余500-50=450
await hardhatToken.connect(addr2).transferFrom(owner.address,addr2.address,50);
expect(await hardhatToken.allowance(owner.address,addr2.address)).to.equal(450);
})
it("check decreaseAllowance", async () => {
//减少addr2的100个配额,还剩下450-100 = 350
await hardhatToken.decreaseAllowance(addr2.address,100);
expect(await hardhatToken.allowance(owner.address,addr2.address)).to.equal(350);
})
it("can not transfer above the amount", async () => {
//addr1只有100份token,却要发送1007份,超过余额,交易会重置
await expect(hardhatToken.connect(addr1).transfer(addr3.address, 1007)).to.be.reverted;
})
});
});
3.3 编译并测试
a) 编译合约
npx hardhat compile
b) 测试合约
npx hardhat test test/erc20/1.HWToken.js
效果如下:
以上是关于ERC20标准函数简介与测试方法的主要内容,如果未能解决你的问题,请参考以下文章