openzeppelin批量测试Solidity合约
openzeppelin是用于Solidity合约进行审计、代码安全测试的库,其中test-environment、test-helpers使用起来非常方便。下面,介绍对ERC20GuDingToken.sol、ERC20XiaoHuiToken.sol这2个合约的批量测试。
1、 创建工程onecoin
1.1 创建工程和文件夹
## 1、创建工程
mkdir onecoin
cd onecoin
npm init -y
truffle init## 2、创建文件夹
mkdir -p test/ERC20
mkdir -p test/inc
mkdir -p contracts/ERC20
mkdir script
1.2 修改package.json
a) 修改onecoin/package.json文件,如下:
{"name": "onecoin","version": "1.0.0","description": "","main": "","directories": {"test": "test"},"scripts": {"test": "node script/test.js","compile": "truffle compile","ganache": "ganache-cli -e 1000","migrate": "truffle migrate","mocha": "mocha --exit --recursive"},"mocha": {"timeout": 100000,"useColors": true,"comment": "这里是彩蛋:下面这一行改成nyan然后再运行npm run test试一下","reporter": "spec"},"keywords": [],"author": "","license": "ISC","dependencies": {"@openzeppelin/contracts": "^2.5.1","@truffle/debug-utils": "^5.1.17","@truffle/hdwallet-provider": "^1.5.0","bip39": "^3.0.4","ethers": "^5.4.7","inquirer": "^8.1.5","web3": "^1.6.0"},"devDependencies": {"@openzeppelin/test-environment": "^0.1.9","@openzeppelin/test-helpers": "^0.5.13","chai": "^4.3.4","eth-gas-reporter": "^0.2.22","mocha": "^9.1.2","@babel/core": "^7.15.5","@babel/preset-env": "^7.15.6"}
}
b) 安装依赖包
npm config set registry https://registry.npm.taobao.org
npm install
1.3 修改truffle-config.js
修改后的truffle-config.js如下:
module.exports = {networks: {// Useful for testing. The `development` name is special - truffle uses it by default// if it's defined here and no other network is specified at the command line.// You should run a client (like ganache-cli, geth or parity) in a separate terminal// tab if you use this network and you must also set the `host`, `port` and `network_id`// options below to some value.//development: {host: "127.0.0.1", // Localhost (default: none)port: 8545, // Standard Ethereum port (default: none)network_id: "*", // Any network (default: none)},// Another network with more advanced options...// advanced: {// port: 8777, // Custom port// network_id: 1342, // Custom network// gas: 8500000, // Gas sent with each transaction (default: ~6700000)// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)// from: <address>, // Account to send txs from (default: accounts[0])// websocket: true // Enable EventEmitter interface for web3 (default: false)// },// Useful for deploying to a public network.// NB: It's important to wrap the provider as a function.// ropsten: {// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),// network_id: 3, // Ropsten's id// gas: 5500000, // Ropsten has a lower block limit than mainnet// confirmations: 2, // # of confs to wait between deployments. (default: 0)// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )// },// Useful for private networks// private: {// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),// network_id: 2111, // This network is yours, in the cloud.// production: true // Treats this network as if it was a public net. (default: false)// }},// Set default mocha options here, use special reporters etc.mocha: {timeout: 300000,reporter: 'eth-gas-reporter',reporterOptions: { excludeContracts: ['Migrations'] }},// Configure your compilerscompilers: {solc: {version: "0.5.12", // Fetch exact version from solc-bin (default: truffle's version)// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)// settings: { // See the solidity docs for advice about optimization and evmVersion// optimizer: {// enabled: false,// runs: 200// },// evmVersion: "byzantium"// }}},// Truffle DB is currently disabled by default; to enable it, change enabled:// false to enabled: true. The default storage location can also be// overridden by specifying the adapter settings, as shown in the commented code below.//// NOTE: It is not possible to migrate your contracts to truffle DB and you should// make a backup of your artifacts to a safe location before enabling this feature.//// After you backed up your artifacts you can utilize db by running migrate as follows: // $ truffle migrate --reset --compile-all//// db: {// enabled: false,// host: "127.0.0.1",// adapter: {// name: "sqlite",// settings: {// directory: ".db"// }// }// }
};
1.4 ERC20GuDingToken合约
路径: onecoin/contracts/ERC20GuDingToken.sol
pragma solidity >=0.4.21 <0.7.0;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";//固定总量Token
contract ERC20GuDingToken is ERC20,ERC20Detailed {constructor(string memory name, //全称 string memory symbol,//简称 uint8 decimals, //精度uint256 totalSupply //总量 ) public ERC20Detailed(name,symbol,decimals) {_mint(msg.sender, totalSupply*(10**uint256(decimals)));}
}
1.5 ERC20XiaoHuiToken合约
路径: onecoin/contracts/ERC20XiaoHuiToken.sol
pragma solidity >=0.4.21 <0.7.0;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";contract ERC20XiaoHuiToken is ERC20,ERC20Detailed,ERC20Burnable {constructor(string memory name, //全称string memory symbol, //简称uint8 decimals, //精度uint256 totalSupply //总量) public ERC20Detailed(name,symbol,decimals) {_mint(msg.sender,totalSupply*(10**uint256(decimals)));}
}
1.6 开启ganache
在 ganache里设置IP:127.0.0.1, 端口:8545,重启ganache,如图(1)所示:
![](/assets/blank.gif)
图(1) 设置并重启ganache
1.7 编写部署脚本
a) ERC20GuDingToken合约的部署脚本:onecoin/migrations/2_deploy_ERC20GuDingToken.js
// 2_deploy_ERC20GuDingToken.js
const ERC20GuDingToken = artifacts.require("ERC20GuDingToken");module.exports = function(deployer) {deployer.deploy(ERC20GuDingToken,"GuDingToken","GDT",18,80000);
};
b) ERC20XiaoHuiToken合约的部署脚本:onecoin/migrations/3_deploy_ERC20XiaoHuiToken.js
// 3_deploy_ERC20XiaoHuiToken.js
const ERC20XiaoHuiToken = artifacts.require("ERC20XiaoHuiToken");module.exports = function(deployer) {deployer.deploy(ERC20XiaoHuiToken,"XiaoHuiToken","XHT",18,80000);
};
1.8 在ganache里部署合约
cd onecoin
truffle console
compile
migrate
1.9 工程目录结构
onecoin的目录结构如下:
![](/assets/blank.gif)
图(2) onecoin的目录结构
2、编写测试脚本
2.1 公共脚本
编写测试ERC20公共功能的脚本: onecoin/test/inc/ERC20.js
//ERC20.js
const assert = require('assert');
const {ether,constants,expectEvent} = require('@openzeppelin/test-helpers');//导出 detail()函数
exports.detail =() => {it('Token名称:name()',async function(){assert.equal(ERC20Param[0], await ERC20Instance.name());});it('Token缩写:symbol()',async function(){assert.equal(ERC20Param[1], await ERC20Instance.symbol());});it('Token精度:decimals()',async function(){assert.equal(ERC20Param[2], (await ERC20Instance.decimals()).toString());});it('Token总量:totalSupply()',async function(){assert.equal(ether(ERC20Param[3]).toString(), (await ERC20Instance.totalSupply()).toString());});
}//测试Token总量
exports.totalSupply =(totalSupply) => {it('Token总量:totalSupply()',async function(){assert.equal(ether(totalSupply).toString(),(await ERC20Instance.totalSupply()).toString());});
}//测试账户余额
exports.balanceOf =(balance,account,desc) => {it(desc + ':balanceOf()',async function(){assert.equal(ether(balance).toString(), (await ERC20Instance.balanceOf(account)).toString());});
}//测试封顶额度
exports.cap =(cap,desc) => {it(desc + ':cap()',async function(){assert.equal(ether(cap).toString(), (await ERC20Instance.cap()).toString());});
}//测试Token发送
exports.transfer =(sender,receiver,amount,desc,reject,msg) => {it(desc + ':transfer()', async function(){if(reject) {await assert.rejects(ERC20Instance.transfer(receiver,ether(amount), {from:sender}), msg);} else {let receipt = await ERC20Instance.transfer(receiver,ether(amount), {from:sender});expectEvent(receipt, 'Transfer', {from: sender,to: receiver,value: ether(amount),});}});
}//测试批准额度
exports.approve = (sender,receiver,amount,desc,reject,msg) => {it(desc + ':approve()', async function(){if(reject) {await assert.rejects(ERC20Instance.approve(receiver,ether(amount),{from:sender}),msg);} else {let receipt = await ERC20Instance.approve(receiver, ether(amount), { from: sender });expectEvent(receipt, 'Approval', {owner: sender,spender: receiver,value: ether(amount),});}});
}//测试批准发送
exports.transferFrom =(owner,sender,receiver,amount,desc,reject,msg) => {it(desc + ':transferFrom()', async function(){if (reject) {await assert.rejects(ERC20Instance.transferFrom(owner,receiver,ether(amount),{from:sender}), msg);} else {let receipt = await ERC20Instance.transferFrom(owner,receiver,ether(amount),{from:sender});expectEvent(receipt, 'Transfer', {from: owner,to: receiver,value: ether(amount),});}});
}//测试批准数额
exports.allowance = (owner,sender,amount,desc) => {it(desc + ':allowance()',async function(){assert.equal(ether(amount), (await ERC20Instance.allowance(owner,sender)).toString());});
}//测试增加批准数额
exports.increaseAllowance = (sender,receiver,amount,desc,reject,msg) => {it(desc + ':increaseAllowance()', async function() {if(reject) {await assert.rejects(ERC20Instance.increaseAllowance(receiver,ether(amount), {from:sender}),msg);} else {let receipt = await ERC20Instance.increaseAllowance(receiver,ether(amount),{from:sender});expectEvent(receipt,'Approval',{owner:sender,spender: receiver,});}});
}//批准减少批准额度
exports.decreaseAllowance = (sender,receiver,amount,desc,reject,msg) => {it(desc + ': decreaseAllowance()', async function () {if(reject) {await assert.rejects(ERC20Instance.decreaseAllowance(receiver, ether(amount), { from: sender }), msg);} else {let receipt = await ERC20Instance.decreaseAllowance(receiver,ether(amount),{from:sender});expectEvent(receipt,'Approval',{owner: sender,spender:receiver,});}});
}//测试销毁方法
exports.burn = (sender,amount,desc,reject,msg) => {it(desc + ':burn()',async function(){if(reject){await assert.rejects(ERC20Instance.burn(ether(amount),{from:sender}), msg);} else {let receipt = await ERC20Instance.burn(ether(amount), { from: sender });expectEvent(receipt, 'Transfer', {from: sender,to: constants.ZERO_ADDRESS,value: ether(amount),});}});
}//测试销毁批准方法
exports.burnFrom = (owner,sender,amount,desc,reject,msg) => {it(desc + ':burnFrom()',async function(){if(reject){await assert.rejects(ERC20Instance.burnFrom(owner,ether(amount),{from:sender}),msg);} else {let receipt = await ERC20Instance.burnFrom(owner,ether(amount),{from:sender});expectEvent(receipt,'Transfer',{from: owner,to: constants.ZERO_ADDRESS,value: ether(amount),});expectEvent(receipt,'Approval', {owner: owner,spender: sender,});}});
}//测试铸币方法
exports.mint = (owner,beneficiary,amount,desc,reject,msg) => {it(desc + ':mint()',async function(){if(reject){await assert.rejects(ERC20Instance.mint(beneficiary,ether(amount),{from:owner}), msg);} else {let receipt = await ERC20Instance.mint(beneficiary,ether(amount),{from:owner});expectEvent(receipt,'Transfer',{from: constants.ZERO_ADDRESS,to: beneficiary,value: ether(amount),});}});
}//添加暂停管理员
exports.addMinter = (minter,sender,desc,reject, msg) => {it(desc + ':addMinter()',async function(){if(reject){await assert.rejects(ERC20Instance.addMinter(minter,{from:sender}), msg);} else {let receipt = await ERC20Instance.addMinter(minter,{from:sender});expectEvent(receipt, 'MinterAdded',{account: minter});}});
}//给账户添加暂停权
exports.isMinter = (minter,isMinter,desc) => {it(desc + ': isMinter()', async function () {assert.equal(isMinter,await ERC20Instance.isMinter(minter));});
}//撤销暂停管理员
exports.renounceMinter = (minter,desc,reject,msg) => {it(desc + ':renounceMinter()',async function(){if(reject){await assert.rejects(ERC20Instance.renounceMinter({from: minter}), msg);} else {let receipt = await ERC20Instance.renounceMinter({from: minter});expectEvent(receipt, 'MinterRemoved', {account: minter});}});
}//给普通用户,添加暂停权限
exports.addPauser = (pauser, sender, desc, reject, msg) => {it(desc + ':addPauser()',async function(){if(reject){await assert.rejects(ERC20Instance.addPauser(pauser,{from: sender}), msg);} else {let receipt = await ERC20Instance.addPauser(pauser, {from: sender});expectEvent(receipt,'PauserAdded', {account: pauser});}});
}//判断用户是否有暂停权
exports.isPauser = (pauser,isPauser,desc) => {it(desc +':isPauser()', async function(){assert.equal(isPauser, await ERC20Instance.isPauser(pauser));});
}//测序普通用户的暂停权限
exports.renouncePauser = (pauser,desc,reject,msg) => {it(desc + ': renouncePauser()', async function () {if(reject){await assert.rejects(ERC20Instance.renouncePauser({from: pauser}), msg);} else {let receipt = await ERC20Instance.renouncePauser({from: pauser});expectEvent(receipt,'PauserRemoved',{account: pauser});}});
}//测试暂停状态
exports.paused = (paused, desc) => {it(desc + ':paused()', async function(){assert.equal(paused, await ERC20Instance.paused());});
}//测试用户的暂停权限
exports.pause = (pauser,desc,reject, msg) => {it(desc + ': pause()', async function () {if(reject){await assert.rejects(ERC20Instance.pause({from:pauser}), msg);} else {let receipt = await ERC20Instance.pause({from:pauser});expectEvent(receipt,'Paused',{account: pauser});}});
}//测试恢复合约
exports.unpause = (pauser,desc, reject, msg) => {it(desc + ':unpause()',async function(){if(reject){await assert.rejects(ERC20Instance.unpause({from:pauser}),msg);} else {let receipt = await ERC20Instance.unpause({from: pauser});expectEvent(receipt, 'Unpaused', {account: pauser});}});
}
2.2 主菜单脚本
主菜单脚本,用于对选中指定的合约脚本进行测试,或者全部测试
路径: onecoin/script/test.js
const fs = require('fs');
var inquirer = require('inquirer');
const {spawn} = require('child_process');const contractDir = 'test/';
getFiles = async (path) => {const dir = await fs.readdirSync(path,'utf-8');let files = [{name:'全部测试', value:'all'}];dir.forEach(async (e1,index) => {let stat = fs.statSync(path + e1);if (e1 !== 'inc') {if (stat.isDirectory()) {let dirFiles = await fs.readdirSync(path+e1+'/','utf-8');dirFiles.forEach((sube1, index) => {files.push(e1 +'/'+sube1);})} else {files.push(e1);}}})return files;
}main = async () => {const files = await getFiles(contractDir);inquirer.prompt([{type: 'list',name: 'step1',message: '选择要测试的合约',choices: files,}]).then( answers => {let argv;if (answers.step1 !== 'all') {console.log("\033[33mRun:\033[39m " + "truffle test " + contractDir + answers.step1);argv = ["mocha","--exit","--recursive",contractDir + answers.step1];} else {argv = ["mocha","--exit","--recursive"];console.log("\033[33mRun:\033[39m " + "truffle test ");}spawn("npx",argv, {stdio: 'inherit',shell: true});});
}main();
2.3 ERC20GuDingToken测试脚本
路径:onecoin/test/ERC20/ERC20GuDingToken.js
//ERC20GuDingToken.js
const {contract,accounts} = require('@openzeppelin/test-environment');
const {constants} = require('@openzeppelin/test-helpers')
const ERC20Contract = contract.fromArtifact("ERC20GuDingToken");
const ERC20 = require('../inc/ERC20');//总量固定的Token
const totalSupply = '80000';
[owner,sender,receiver,purchaser,beneficiary] = accounts;
EthValue = '10';describe("固定总量Token", function(){it('部署合约', async function(){ERC20Param = ["GuDingToken","GDC",18,totalSupply];ERC20Instance = await ERC20Contract.new(...ERC20Param, {from: owner});});
});describe("测试ERC20合约基本信息", function(){ERC20.detail();
});describe("测试ERC20合约的标准方法",async function(){//测试余额ERC20.balanceOf(totalSupply, owner, '创建者账户余额');//测试发送ERC20.transfer(owner, constants.ZERO_ADDRESS, EthValue, 'Token发送,0地址错误', true, 'ERC20: transfer to the zero address');ERC20.transfer(owner, receiver, EthValue, 'Token发送');//测试超额发送ERC20.transfer(owner, receiver, totalSupply, '超额发送错误', true, 'ERC20: transfer amount exceeds balance');//测试余额ERC20.balanceOf(EthValue, receiver, '接收者账户余额');//receiver.balance = value//测试批准ERC20.approve(owner, constants.ZERO_ADDRESS, EthValue, '批准Token,0地址错误', true, 'ERC20: approve to the zero address');ERC20.approve(receiver, purchaser, EthValue, '批准Token');//验证批准ERC20.allowance(receiver, purchaser, EthValue, '验证批准数额');//receiver=>purchaser = value//测试传送批准ERC20.transferFrom(receiver, purchaser, beneficiary, EthValue, '批准发送');//beneficiary.balance = value//测试余额ERC20.balanceOf(EthValue, beneficiary, '接收者账户余额');//receiver.balance = value//测试超额发送批准ERC20.transferFrom(receiver, purchaser, beneficiary, EthValue, '超额批准发送', true, 'ERC20: transfer amount exceeds balance');//验证批准归零ERC20.allowance(receiver, purchaser, '0', '批准额归零');//receiver=>purchaser = 0//增加批准ERC20.increaseAllowance(receiver, purchaser, EthValue, '增加批准额');//验证批准ERC20.allowance(receiver, purchaser, EthValue, '验证批准数额');//receiver=>purchaser = value//减少批准ERC20.decreaseAllowance(receiver, purchaser, EthValue, '减少批准额');//验证批准ERC20.allowance(receiver, purchaser, '0', '批准数额归零');//receiver=>purchaser = 0//超额减少批准ERC20.decreaseAllowance(receiver, purchaser, EthValue, '超额减少批准额', true, 'ERC20: decreased allowance below zero');});
2.4 ERC20XiaoHuiToken测试脚本
路径: onecoin/test/ERC20/ERC20XiaoHuiToken.js
// ERC20XiaoHuiToken.js
const {contract,accounts} = require('@openzeppelin/test-environment');
const {constants} = require('@openzeppelin/test-helpers')
const ERC20Contract = contract.fromArtifact("ERC20XiaoHuiToken");
const ERC20 = require('../inc/ERC20');//可销毁的Token
const totalSupply = '80000';
[owner,sender,receiver,purchaser,beneficiary] = accounts;
EthValue = '10';describe("可销毁的Token", function(){it('部署合约', async function(){ERC20Param = ["XiaoHuiToken","XHT",18,totalSupply];ERC20Instance = await ERC20Contract.new(...ERC20Param, {from: owner});});
});describe("测试ERC20合约基本信息", function(){ERC20.detail();
});describe("测试ERC20合约的标准方法", async function () {//测试余额ERC20.balanceOf(totalSupply, owner, '创建者账户余额');//测试发送ERC20.transfer(owner, constants.ZERO_ADDRESS, EthValue, 'Token发送,0地址错误', true, 'ERC20: transfer to the zero address');ERC20.transfer(owner, receiver, EthValue, 'Token发送');//测试超额发送ERC20.transfer(owner, receiver, totalSupply, '超额发送错误', true, 'ERC20: transfer amount exceeds balance');//测试余额ERC20.balanceOf(EthValue, receiver, '接收者账户余额');//receiver.balance = value//测试批准ERC20.approve(owner, constants.ZERO_ADDRESS, EthValue, '批准Token,0地址错误', true, 'ERC20: approve to the zero address');ERC20.approve(receiver, purchaser, EthValue, '批准Token');//验证批准ERC20.allowance(receiver, purchaser, EthValue, '验证批准数额');//receiver=>purchaser = value//测试传送批准ERC20.transferFrom(receiver, purchaser, beneficiary, EthValue, '批准发送');//beneficiary.balance = value//测试余额ERC20.balanceOf(EthValue, beneficiary, '接收者账户余额');//receiver.balance = value//测试超额发送批准ERC20.transferFrom(receiver, purchaser, beneficiary, EthValue, '超额批准发送', true, 'ERC20: transfer amount exceeds balance');//验证批准归零ERC20.allowance(receiver, purchaser, '0', '批准额归零');//receiver=>purchaser = 0//增加批准ERC20.increaseAllowance(receiver, purchaser, EthValue, '增加批准额');//验证批准ERC20.allowance(receiver, purchaser, EthValue, '验证批准数额');//receiver=>purchaser = value//减少批准ERC20.decreaseAllowance(receiver, purchaser, EthValue, '减少批准额');//验证批准ERC20.allowance(receiver, purchaser, '0', '批准数额归零');//receiver=>purchaser = 0//超额减少批准ERC20.decreaseAllowance(receiver, purchaser, EthValue, '超额减少批准额', true, 'ERC20: decreased allowance below zero');
});describe("测试ERC20合约的销毁方法", async function(){ERC20.burn(beneficiary, EthValue, '销毁代币');ERC20.balanceOf('0', beneficiary, '销毁后余额归零');ERC20.burn(beneficiary, EthValue, '超额销毁', true, 'ERC20: burn amount exceeds balance');ERC20.approve(owner, receiver, EthValue, '增加批准额');ERC20.allowance(owner, receiver, EthValue, '测试批准数额');//owner=>receiver = valueERC20.burnFrom(owner, receiver, EthValue, '销毁批准额');ERC20.allowance(owner, receiver, '0', '销毁销毁后批准额归零');//owner=>receiver = 0ERC20.burnFrom(owner, receiver, EthValue, '超额销毁批准额', true, 'ERC20: burn amount exceeds allowance');
});
3、进行测试
打开一个控制台终端,输入如下命令:
npm run test## 或者使用命令:
node scripts\test.js## 选中"全部测试" 表示测试contracts目录里的所有合约
## 选中"ERC20/ERC20GuDingToken.js" 表示测试ERC20GuDingToken合约
## 选中"ERC20/ERC20XiaoHuiToken.js" 表示测试ERC20XiaoHuiTokenn合约
![](/assets/blank.gif)
图(3) 按上下方向键,选中要测试的合约
这里,选中“全部测试”一栏,效果如下:
? 选择要测试的合约 全部测试
Run: truffle test固定总量Token✔ 部署合约 (358ms)测试ERC20合约基本信息✔ Token名称:name() (166ms)✔ Token缩写:symbol() (154ms)✔ Token精度:decimals() (141ms)✔ Token总量:totalSupply() (127ms)测试ERC20合约的标准方法✔ 创建者账户余额:balanceOf() (151ms)✔ Token发送,0地址错误:transfer() (226ms)✔ Token发送:transfer() (192ms)✔ 超额发送错误:transfer() (229ms)✔ 接收者账户余额:balanceOf() (122ms)✔ 批准Token,0地址错误:approve() (247ms)✔ 批准Token:approve() (185ms)✔ 验证批准数额:allowance() (79ms)✔ 批准发送:transferFrom() (216ms)✔ 接收者账户余额:balanceOf() (138ms)✔ 超额批准发送:transferFrom() (282ms)✔ 批准额归零:allowance() (137ms)✔ 增加批准额:increaseAllowance() (182ms)✔ 验证批准数额:allowance() (160ms)✔ 减少批准额: decreaseAllowance() (137ms)✔ 批准数额归零:allowance() (121ms)✔ 超额减少批准额: decreaseAllowance() (363ms)可销毁的Token✔ 部署合约 (490ms)测试ERC20合约基本信息✔ Token名称:name() (77ms)✔ Token缩写:symbol() (96ms)✔ Token精度:decimals() (151ms)✔ Token总量:totalSupply() (155ms)测试ERC20合约的标准方法✔ 创建者账户余额:balanceOf() (138ms)✔ Token发送,0地址错误:transfer() (235ms)✔ Token发送:transfer() (217ms)✔ 超额发送错误:transfer() (291ms)✔ 接收者账户余额:balanceOf() (113ms)✔ 批准Token,0地址错误:approve() (262ms)✔ 批准Token:approve() (182ms)✔ 验证批准数额:allowance() (114ms)✔ 批准发送:transferFrom() (273ms)✔ 接收者账户余额:balanceOf() (109ms)✔ 超额批准发送:transferFrom() (195ms)✔ 批准额归零:allowance() (147ms)✔ 增加批准额:increaseAllowance() (172ms)✔ 验证批准数额:allowance() (150ms)✔ 减少批准额: decreaseAllowance() (267ms)✔ 批准数额归零:allowance() (154ms)✔ 超额减少批准额: decreaseAllowance() (201ms)测试ERC20合约的销毁方法✔ 销毁代币:burn() (222ms)✔ 销毁后余额归零:balanceOf() (137ms)✔ 超额销毁:burn() (273ms)✔ 增加批准额:approve() (251ms)✔ 测试批准数额:allowance() (110ms)✔ 销毁批准额:burnFrom() (141ms)✔ 销毁销毁后批准额归零:allowance() (155ms)✔ 超额销毁批准额:burnFrom() (369ms)52 passing (10s)
由上可知,这2个合约,一共52测试案例,全部测试通过,耗时10s。
openzeppelin批量测试Solidity合约相关推荐
- 【区块链】Truffle 部署 编译 测试 智能合约 的 完整实践操作
本文首发自我的CSDN博客,原文链接如下 blog.csdn.net/diandianxiy- 目标 搭建开发环境 创建一个Truffle项目 编写智能合约 编译转移智能合约 测试智能合约 创建用户界 ...
- 剖析Solidity合约创建EVM bytecode
1. 引言 前序博客有: Ethereum EVM简介 揭秘EVM Opcodes 在以太坊中,当合约创建时,init code将作为交易的一部分发送,然后返回该合约的实际bytecode--runt ...
- Truffle - 2 利用Truffle编写、测试智能合约并将其部署到不同的测试网络
2 利用Truffle编写.测试智能合约并将其部署到不同的测试网络 2.1创建项目 建一个文件夹 mkdir truffle-project truffle init //初始化qinjianquan ...
- 用Remix部署Solidity合约
Remix是一个用于开发和部署Solidity合约的线上IDE,该IDE含有各个版本的Solidity,当一个工程有多个Solidity版本时,就需要使用Remix来进行分开编译和部署. 1. ...
- 智能合约场景下的模糊测试——智能合约基本介绍
智能合约场景下的模糊测试--智能合约基本介绍 前言 一 基本概念 1.1 智能合约 1.2 图灵完全 二 智能合约特性 2.1 运行环境 2.2 生命周期 1)开发 2)编译 3)部署 4)调用 5) ...
- 1. 验证集 -- 批量测试和可视化 2. 测试集 -- 批量测试和可视化
1.验证集val.txt验证测试结果可视化~ 一步到位!!!批量验证集测试!!! import sys sys.path.append("..") sys.path.insert( ...
- java+测试ip是否通_java 批量测试主机能否ping通
本文为批量测试主机能够ping通,可以作为测试哪些IP是没有被使用的,或者验证哪些IP是被绑定到主机的. 下面直接上代码:package com.fengPay.demo; import java.i ...
- 亲试:darknet_yolov3批量测试图片并保存在自定义文件夹下与图片视频相互转换
使用darknet批量测试图片并保存在指定文件夹下 测试时:Makefile前五行一定全调为0 当我们使用darknet框架使用测试语句时,系统调用程序语句,我们需要的是加入可以连续调用图片的系统,在 ...
- 批量ping指定端口,批量测试IP地址是否通
IP地址批量测试,ping的小记录 测试一个ip通不通,首选用ping命令,格式:ping [ip] 比如我测试 192.168.1.1 这个ip地址,如下: D:\>ping 192.168 ...
最新文章
- 习题8-6 删除字符 (20 分)
- 微积分学习笔记四:空间向量基础
- 第二关练习题总结完结
- 【Python 自然语言处理 第二版】读书笔记2:获得文本语料和词汇资源
- OSG造成屏幕闪烁,且鼠标不能操作的一行代码
- java做一个简单的数据库,哪个嵌入式数据库用Java写成一个简单的键/值存储?
- git 简单操作流程图
- 解决zabbix可用性为灰色状态
- 长方形旋转html5,HTML5/SVG旋转长方形来得到六边形图案
- JS内置对象练习(慕课网题目)
- C语言:编程打印图形
- win10蓝牙无法连接
- python实现视频的实时传送_基于python实现高速视频传输程序
- vue 集成环信 web im 加 一对一视频通话
- 如何免费在线听周杰伦的歌曲
- Vue实现 TodoList
- 别学英语了!微软给PPT和Skype新加的这个功能,让你和老外从此交流无障碍
- Unity制作九宫格手机手势解锁密码
- 苹果蓝牙耳机平替哪款最好?四款苹果蓝牙耳机平价替代
- Cesium深入浅出之图层管理器