以太坊开发(十八)使用Truffle+OpenZeppelin编写并测试ERC20代币

创建项目

有别于之前使用truffle init指令来初始化项目,在Truffle推出Boxes功能之后,我们可以直接套用称作react-box的样板,此样板已经整合create-react-app,可以直接用它来开发react web,省下项目设置的时间。

yuyangdeMacBook-Pro:~ yuyang$ cd /Users/yuyang/BloggerCoin 
yuyangdeMacBook-Pro:BloggerCoin yuyang$ truffle unbox react-box
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:              truffle compile
  Migrate:              truffle migrate
  Test contracts:       truffle test
  Test dapp:            npm test
  Run dev server:       npm run start
  Build for production: npm run build

目录结构

  • /contracts:存放智能合约原始码的地方,可以看到里面已经有放两个sol文件。我们开发的BloggerCoin.sol也会放在这里。

  • /migrations:这是Truffle用来部署智能合约的功能,待会我们会修改2_deploy_contracts.js来部署BloggerCoin.sol

  • /test:测试智能合约的代码放这目录,支持jssol测试。

  • /public/src:存放react web的地方,后面用到会再说明。

  • truffle.js: Truffle的设置文件。

开发前的准备

  1. 打开终端,启动testrpc,继续通过testrpc模拟以太坊区块链测试环境(Ganache已经取代了testrpc,可以使用Ganache或者truffle develop模拟测试环境。查看之前的文章获取帮助)。

  2. 创建的代币如果想要能够通过以太币钱包来进行转账和收帐,必须兼容于以太坊的ERC20标准,ERC20定义了支持钱包所必需的合约界面。

  3. 在本篇文章中,我们将安装OpenZeppelin来简化加密钱包开发的过程。OpenZeppelin是一套能够给我们方便提供编写加密合约的函数库,同时里面也提供了兼容ERC20的智能合约。

安装zeppelin-solidity

yuyangdeMacBook-Pro:BloggerCoin yuyang$ npm install zeppelin-solidity
npm WARN The package dotenv is included as both a dev and production dependency.

+ [email protected]
added 3 packages in 16.261s

Atom打开项目查看zeppelin-solidity安装结果

通过Atom打开项目,在node_modules中的最后一个文件夹就是zeppelin-solidity的内容。

创建标准的代币合约

contracts/目录下建立一个BloggerCoin.sol文件。也可以使用以下命令来创建文件:

yuyangdeMacBook-Pro:BloggerCoin yuyang$ truffle create contract BloggerCoin

BloggerCoin.sol代码如下:

pragma solidity ^0.4.4;
import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol";

contract BloggerCoin is StandardToken {
  string public name = "BloggerCoin";
  string public symbol = "BLC";
  uint8 public decimals = 2;
  uint256 public INITIAL_SUPPLY = 666666;

  function BloggerCoin() public {
    totalSupply_ = INITIAL_SUPPLY;
    balances[msg.sender] = INITIAL_SUPPLY;
  }
}

这里在ATOM中会报错,找不到StandardToken.sol文件。但是不影响truffle的编译。

代码解释:

pragma solidity ^0.4.4;

第一行代表solidity的版本,不同的版本编译的字节码不一样,^代表向上兼容,不过版本不能超过0.5.0

import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol";

这句代码是通过import来导入我们需要使用到的StandardToken合约。

contract BloggerCoin is StandardToken {
    ...
}

建立BloggerCoin合约时,让BloggerCoin合约直接继承自StandardTokenis既是继承。因此BloggerCoin继承了StandardToken所有的状态数据和方法。

当我们继承了StandardToken合约,也就支持了以下ERC20标准中规定的函数。

函数 方法
totalSupply() 代币发行的总量
balanceOf(A) 查询A帐户下的代币数目
transfer(A,x) 发送x个代币到A帐户
transferFrom(A,x) 从A帐户提取x个代币
approve(A,x) 同意A帐户从我的帐户中提取代币
allowance(A,B) 查询B帐户可以从A帐户提取多少代币

和之前一样,后面验证时会用到balanceOftransfer两个函数。因为StandardToken合约中已经帮我们实现了这些函数,因此我们不需要自己从头再写一次。

string public name = "BloggerCoin";
string public symbol = "BLC";
uint8 public decimals = 2;
uint256 public INITIAL_SUPPLY = 666666;

这边设定参数的目的是指定这个代币的一些特性。以人民币为例,人民币的名称(name)是RMB,美元的代号为¥,拿1元去找零最小可以拿到零钱是一分,也就是0.01元。因为1元最小可分割到小数点后2(0.01),因此最小交易单位(decimals)2

这里将这个加密代币取名(name)为BloggerCoin(部落币),代币的代号(symbol)为BLC,最小分割单位是2(最小可以找0.01个部落币)。

以下为人民币,比特币,以太币,部落币的对照表供参考:

name symbol decimals
RMB ¥ 4
Bitcoin BTC 8
Ethereum ETH 18
BloggerCoin BLC 2

最后也定义了初始代币数目INITIAL_SUPPLY。这里选择了一个吉祥数字666666。另外,当我们把全局变量设为public(公开),编译时就会自动新增一个读取公开变量的ABI接口,我们在truffle console中也可以读取这些变量。

function BloggerCoin() {
    totalSupply_ = INITIAL_SUPPLY;
    balances[msg.sender] = INITIAL_SUPPLY;
}

和合约同名的BloggerCoin方法,就是BloggerCoin合约的构造函函数(constructor)。在构造函数里指定了totalSupply_数目,并将所有的初始代币INITIAL_SUPPLY都指定给msg.sender帐号,也就是用来部署这个合约的帐号。totalSupply_balances定义于BasicToken.sol中。

pragma solidity ^0.4.18;


import "./ERC20Basic.sol";
import "../../math/SafeMath.sol";


/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
contract BasicToken is ERC20Basic {
  using SafeMath for uint256;

  mapping(address => uint256) balances;

  uint256 totalSupply_;

  /**
  * @dev total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return totalSupply_;
  }

  /**
  * @dev transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[msg.sender]);

    // SafeMath.sub will throw if there is not enough balance.
    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    Transfer(msg.sender, _to, _value);
    return true;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256 balance) {
    return balances[_owner];
  }

}

进一步追去看BasicToken.sol合约的内容,可以发现BasicToken.sol合约中导入了SafeMath.sol合约。SafeMath对各种数值运算加入了必要的验证,让合约中的数字计算更安全。

如此一来,我们已写好一个可通过以太币钱包交易的新加密代币合约。这个合约一经部署,就可以一直存在于以太坊区块链上,世界上从此也就多了一种新的加密代币。只要你能找到人想拥有这种代币,这种代币就有交易的价值。

编译、部署、验证

migrations/目录下建立一个3_deploy_bloggercoin.js文件,内容如下:

var BloggerCoin = artifacts.require("./BloggerCoin.sol");

module.exports = function(deployer) {
  deployer.deploy(BloggerCoin);
};

开启truffle develop,模拟测试网络:

yuyangdeMacBook-Pro:BloggerCoin yuyang$ truffle develop
Truffle Develop started at http://127.0.0.1:9545/

Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de

Private Keys:
(0) c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3
(1) ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f
(2) 0dbbe8e4ae425a6d2687f1a7e3ba17bc98c673636790f1b8ad91193c05875ef1
(3) c88b703fb08cbea894b6aeff5a544fb92e78a18e19814cd85da83b71f772aa6c
(4) 388c684f0ba1ef5017716adb5d21a053ea8e90277d0868337519f97bede61418
(5) 659cbb0e2411a44db63778987b1e22153c086a95eb6b18bdf89de078917abc63
(6) 82d052c865f5763aad42add438569276c00d3d88a2d062d36b2bae914d58b8c8
(7) aa3680d5d48a8283413f7a108367c7299ca73f553735860a87b08f39395618b7
(8) 0f62d96d6675f32685bbdb8ac13cda7c23436f63efbb9d07700d8669ff12b7c4
(9) 8d5366123cb560bb606379f90a0bfd4769eecc0557f1b362dcae9012b548b1e5

Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat

⚠️  Important ⚠️  : This mnemonic was created for you by Truffle. It is not secure.
Ensure you do not use it on production blockchains, or else you risk losing funds.

truffle(develop)> 

truffle compile 编译:

truffle(develop)> compile
Compiling ./contracts/BloggerCoin.sol...
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/SimpleStorage.sol...
Compiling zeppelin-solidity/contracts/math/SafeMath.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/BasicToken.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/ERC20.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/StandardToken.sol...
Writing artifacts to ./build/contracts

truffle migrate 部署:

truffle(develop)> migrate
Using network 'develop'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xa2bc55eadab727fd49776fce33779e32eb7ccb89e7138d1a5b272d9e352209f8
  Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
  ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying SimpleStorage...
  ... 0xdefa84918abe976e6404c9cf5ecfa86fbf5e194e1801447b37bd697b25692df6
  SimpleStorage: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
  ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...
Running migration: 3_deploy_bloggercoin.js
  Deploying BloggerCoin...
  ... 0x60dda8ead64aabcba003ae1d212d3f4430dbaf90bb57387b7b0356cd68dc0c64
  BloggerCoin: 0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f
Saving successful migration to network...
  ... 0x55b13865c62964c3b211dd22c1106101855e698e00dbf6d21aad05d6707cf626
Saving artifacts...

验证:

truffle(develop)> let contract
undefined
truffle(develop)> BloggerCoin.deployed().then(instance => contract = instance)
... ...
truffle(develop)> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 666666 ] }
truffle(develop)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
truffle(develop)> contract.transfer(web3.eth.accounts[1], 600000)
{ tx: '0x650a1535dc5adaa946358c627321686d799894babbfee5001f301c91ec1225dc',
  receipt: 
   { transactionHash: '0x650a1535dc5adaa946358c627321686d799894babbfee5001f301c91ec1225dc',
     transactionIndex: 0,
     blockHash: '0x75285b3a555f5f677d0f1915cd0b7af1343d6d5d1025fb60ce98a75860b1d61b',
     blockNumber: 7,
     gasUsed: 51669,
     cumulativeGasUsed: 51669,
     contractAddress: null,
     logs: [ [Object] ],
     status: '0x01',
     logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000008000000000008000000000000000080000000000000000000000000000000000000000000000000000000000000000010000000000000000000010000000000000000400000000000000000000000010000000002000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000' },
  logs: 
   [ { logIndex: 0,
       transactionIndex: 0,
       transactionHash: '0x650a1535dc5adaa946358c627321686d799894babbfee5001f301c91ec1225dc',
       blockHash: '0x75285b3a555f5f677d0f1915cd0b7af1343d6d5d1025fb60ce98a75860b1d61b',
       blockNumber: 7,
       address: '0x8f0483125fcb9aaaefa9209d8e9d7b9c8b9fb90f',
       type: 'mined',
       event: 'Transfer',
       args: [Object] } ] }
truffle(develop)> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 4, c: [ 66666 ] }
truffle(develop)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 5, c: [ 600000 ] }

参考:如何编写智能合约(Smart Contract)?(III)建立标准代币部落币「BLC」
作者:黎跃春

你可能感兴趣的:(以太坊开发(十八)使用Truffle+OpenZeppelin编写并测试ERC20代币)