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)所示:

图(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的目录结构如下:

图(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合约

图(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合约相关推荐

  1. 【区块链】Truffle 部署 编译 测试 智能合约 的 完整实践操作

    本文首发自我的CSDN博客,原文链接如下 blog.csdn.net/diandianxiy- 目标 搭建开发环境 创建一个Truffle项目 编写智能合约 编译转移智能合约 测试智能合约 创建用户界 ...

  2. 剖析Solidity合约创建EVM bytecode

    1. 引言 前序博客有: Ethereum EVM简介 揭秘EVM Opcodes 在以太坊中,当合约创建时,init code将作为交易的一部分发送,然后返回该合约的实际bytecode--runt ...

  3. Truffle - 2 利用Truffle编写、测试智能合约并将其部署到不同的测试网络

    2 利用Truffle编写.测试智能合约并将其部署到不同的测试网络 2.1创建项目 建一个文件夹 mkdir truffle-project truffle init //初始化qinjianquan ...

  4. 用Remix部署Solidity合约

        Remix是一个用于开发和部署Solidity合约的线上IDE,该IDE含有各个版本的Solidity,当一个工程有多个Solidity版本时,就需要使用Remix来进行分开编译和部署. 1. ...

  5. 智能合约场景下的模糊测试——智能合约基本介绍

    智能合约场景下的模糊测试--智能合约基本介绍 前言 一 基本概念 1.1 智能合约 1.2 图灵完全 二 智能合约特性 2.1 运行环境 2.2 生命周期 1)开发 2)编译 3)部署 4)调用 5) ...

  6. 1. 验证集 -- 批量测试和可视化 2. 测试集 -- 批量测试和可视化

    1.验证集val.txt验证测试结果可视化~ 一步到位!!!批量验证集测试!!! import sys sys.path.append("..") sys.path.insert( ...

  7. java+测试ip是否通_java 批量测试主机能否ping通

    本文为批量测试主机能够ping通,可以作为测试哪些IP是没有被使用的,或者验证哪些IP是被绑定到主机的. 下面直接上代码:package com.fengPay.demo; import java.i ...

  8. 亲试:darknet_yolov3批量测试图片并保存在自定义文件夹下与图片视频相互转换

    使用darknet批量测试图片并保存在指定文件夹下 测试时:Makefile前五行一定全调为0 当我们使用darknet框架使用测试语句时,系统调用程序语句,我们需要的是加入可以连续调用图片的系统,在 ...

  9. 批量ping指定端口,批量测试IP地址是否通

    IP地址批量测试,ping的小记录 测试一个ip通不通,首选用ping命令,格式:ping [ip] 比如我测试 192.168.1.1  这个ip地址,如下: D:\>ping 192.168 ...

最新文章

  1. 习题8-6 删除字符 (20 分)
  2. 微积分学习笔记四:空间向量基础
  3. 第二关练习题总结完结
  4. 【Python 自然语言处理 第二版】读书笔记2:获得文本语料和词汇资源
  5. OSG造成屏幕闪烁,且鼠标不能操作的一行代码
  6. java做一个简单的数据库,哪个嵌入式数据库用Java写成一个简单的键/值存储?
  7. git 简单操作流程图
  8. 解决zabbix可用性为灰色状态
  9. 长方形旋转html5,HTML5/SVG旋转长方形来得到六边形图案
  10. JS内置对象练习(慕课网题目)
  11. C语言:编程打印图形
  12. win10蓝牙无法连接
  13. python实现视频的实时传送_基于python实现高速视频传输程序
  14. vue 集成环信 web im 加 一对一视频通话
  15. 如何免费在线听周杰伦的歌曲
  16. Vue实现 TodoList
  17. 别学英语了!微软给PPT和Skype新加的这个功能,让你和老外从此交流无障碍
  18. Unity制作九宫格手机手势解锁密码
  19. 苹果蓝牙耳机平替哪款最好?四款苹果蓝牙耳机平价替代
  20. Cesium深入浅出之图层管理器

热门文章

  1. MovePrevious和MoveNext的用法
  2. 实战干货!用 Python 爬取股票实时数据!
  3. 怎么编写properties文件
  4. 如何使用Python访问和查询Google BigQuery数据
  5. eolinker 的安装部署
  6. EolinkerAPI测试
  7. GeoMesa源码学习:空间索引
  8. php 正则表达式函数库
  9. 不同时期Mac机型硬件性能配置对比
  10. 开启微信小程序的学习窗口(第一课)