接上一篇编写智能合约的教程带你玩转以太坊智能合约的”Hello World”,我们现在要建立一个简单的代币系统,具备最简单的转账和查询功能。

1.启动testrpc测试环境

sily@lyg-sily:~$ testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0x73b5b6e153e03ec11eeeb3d623e50867a5e5227e
(1) 0xfdc2bb26d860ac1f84f3f06826ab1da4b3ef30b6
(2) 0x60e468066c781c1864479fb20cae2698eacc784b
(3) 0x2ea86e060b90cbe10cdba24e79496e6c46a483c7
(4) 0x51ada420bddc3a03c1294645784096bf663545b3
(5) 0xb8eaaf1449510e6d91229ddc6561d54a5dabef84
(6) 0xa1ab09f35f701ca1b91a8ae6ac800305a078e5ae
(7) 0xc84468b9c9f2a3d4e9c3a7861caa77a7f12593dd
(8) 0xd671b2eb8b0d0425b9dcc764bfee4fc8f19e6d5a
(9) 0x0351bb8bd2033ae15b8aee1ddf7d037fe12fd88c

Private Keys
==================
(0) eee65f7557d7c1271a2cf5848d816770bfcb18dab7c353858487299be4cf3f71
(1) 4391df2085e6c810c5b3125f37c81287e4697ed4076f2cfbe10f90cc23ac15e7
(2) 0ef1c08f8be81d79420ee3af55e657716ad79a9495c24e10a6ff8864c141c0fc
(3) da5766fd22e3b53b395d32ec9e544a15169f3ec54a1332c2146acf0002b37326
(4) fc4440a9fdc2e3bf9c6ab20107ef567433eaff1133d703c83fd26efb8b727ed3
(5) 88230785f8b2814659bc824b93e29bcf928e1af4371d53180fb276e8c8592402
(6) 3ce639e515d36811e45ccdd578732ec0799ba09156419d9c09a8b4a7029d70de
(7) 59ca1ade2f0a701a7d1e14ec5b886e9a835fea7d0aedf7226fa83cfe52c1e1fd
(8) c819bd2bdaeb8cc84f8b79ee0ff9089b99c725d6aca91ff793f2bc9c4df82243
(9) 376d808c1b708c126c45aa890ad4d208ac24cbe922fe5c97c952022e153423b4

HD Wallet
==================
Mnemonic:      negative sea robot enforce tube cattle public engage jewel grit install fatigue
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

2.建立项目

前面我们搭建了智能合约的testrpc测试环境,并通过truffle框架进行了简单的HelloWorld代码开发、编译、部署以及调用合约。接下来,我们要创建一个简单的代币,以继续熟悉truffle框架和solidity语法。

首先,回顾一下上次建立HelloWorld项目的命令:

sily@lyg-sily:~$ mkdir SmartContractDemo
sily@lyg-sily:~$ cd SmartContractDemo/
sily@lyg-sily:~/SmartContractDemo$ mkdir HelloWorld
sily@lyg-sily:~/SmartContractDemo$ cd HelloWorld/
sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
sily@lyg-sily:~/SmartContractDemo/HelloWorld$ ls
contracts  migrations  test  truffle-config.js  truffle.js
sily@lyg-sily:~/SmartContractDemo/HelloWorld$

3.编写智能合约

在项目下的contracts/文件夹中新建EncryptedToken.sol,内容如下:

pragma solidity ^0.4.17;

contract EncryptedToken {
  uint256 INITIAL_SUPPLY = 21000000;
  mapping (address => uint256) balances;
  function EncryptedToken() {
    balances[msg.sender] = INITIAL_SUPPLY;
  }

  //转帐到一个指定的地址
  function transfer(address _to, uint256 _amount) {
    assert(balances[msg.sender] >= _amount);
    balances[msg.sender] -= _amount;
    balances[_to] += _amount;
  }

  //查看指定地址的余额
  function balanceOf(address _owner) constant returns (uint256) {
    return balances[_owner];
  }
}

uint256 INITIAL_SUPPLY = 21000000声明变量,将当前合约的钱包地址代币数设置为21000000。

mapping(address => uint256) balances;声明了balances是一个key类型为addressvalue类型为uint256的键值对(mapping)。

function EncryptedToken()函数是EncryptedToken合约的构造函数(contructor),当EncryptedToken合约调用时,会先执行构造函数中的内容。在构造函数中,会以当前部署合约的钱包地址为key,以INITIAL_SUPPLYvalue初始化一个键值对。

transfer函数声明了转账到指定钱包地址的函数,_to是转账的目标地址,_amount表示转账金额。

assert断言确保当前钱包余额不少于要转账的金额时才能进行转账,否则会抛出异常。

balanceOf(address _owner)函数是用来查询指定钱包地址的余额,_owner即是指定的钱包地址,return (uint256)代表返回值的类型是uint256constant关键字说明了函数是只读的,调用时不会消耗手续费。

4.编译部署

migrations/目录下创建一个3_deploy_contract.js的文件。内容如下:

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

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

然后我们执行truffle compiletruffle migrate命令:

sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle compile
Compiling ./contracts/EncryptedToken.sol...
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/Migrations.sol...

Compilation warnings encountered:

/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol:6:3: Warning: No visibility specified. Defaulting to "public".
  function EncryptedToken() {
  ^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol:11:3: Warning: No visibility specified. Defaulting to "public".
  function transfer(address _to, uint256 _amount) {
  ^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol:18:3: Warning: No visibility specified. Defaulting to "public".
  function balanceOf(address _owner) constant returns (uint256) {
  ^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:4:3: Warning: Function state mutability can be restricted to pure
  function sayHello() public constant returns (string) {
  ^
Spanning multiple lines.
,/home/sily/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:8:3: Warning: Function state mutability can be restricted to pure
  function echo(string name) public constant returns (string) {
  ^
Spanning multiple lines.

Writing artifacts to ./build/contracts

sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle migrate --reset
Using network 'development'.

Running migration: 1_initial_migration.js
  Replacing Migrations...
  ... 0xfc5af4b443dcae96f96206e26d2bc93b9699e7d14980dd0247baea112bd04c44
  Migrations: 0x9681bbaee9346afe05e7ceaf72b32d433b329090
Saving successful migration to network...
  ... 0x2155887e4c7037d33fbfbec1df073152ac72784a6a594886a066d412cf105b66
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Replacing HelloWorld...
  ... 0x9a4cd911a85438c4087ee7144dc2b0eeb9bf88f7985fcf56f9949b41b02f936a
  HelloWorld: 0x644fad81bc1075e4f39dc765f671f81ad363317c
Saving successful migration to network...
  ... 0x19e5b97d493c652fc8f67c53c7a56899c8285127b2e70a1bfd41f7b8d35c067d
Saving artifacts...
Running migration: 3_deploy_contracts.js
  Deploying EncryptedToken...
  ... 0x3206c798ab3d0e0ca5e49865bb0cbd207513c1907539c5c921cc027125639a4e
  EncryptedToken: 0xeff776cc1aa2f01c40e638538fdfaadbfb179b00
Saving successful migration to network...
  ... 0x4fb48f957d364f77510f2acc82467dca4e70d268078db8418dfae78000523cd9
Saving artifacts...

5.合约验证

合约部署完成后,输入truffle console命令开启console控制台。在控制台中对已部署的合约进行验证。

web3.eth.coinbaseweb3.eth.accounts[0]表示testrpc环境初始分配的首个钱包地址,也可以通过索引值获取其他9个钱包地址。

EncryptedToken.deployed().then(instance => contract = instance)声明了合约变量contract来存储EncryptedToken合约实例。

sily@lyg-sily:~/SmartContractDemo/HelloWorld$ truffle console
truffle(development)> let contract;
undefined
truffle(development)> EncryptedToken.deployed().then(instance => contract = instance)
TruffleContract {
  constructor:
   { [Function: TruffleContract]
     _static_methods:
      { setProvider: [Function: setProvider],
        new: [Function: new],
        at: [Function: at],
        deployed: [Function: deployed],
        defaults: [Function: defaults],
        hasNetwork: [Function: hasNetwork],
        isDeployed: [Function: isDeployed],
        detectNetwork: [Function: detectNetwork],
        setNetwork: [Function: setNetwork],
        resetAddress: [Function: resetAddress],
        link: [Function: link],
        clone: [Function: clone],
        addProp: [Function: addProp],
        toJSON: [Function: toJSON] },
     _properties:
      { contract_name: [Object],
        contractName: [Object],
        abi: [Object],
        network: [Function: network],
        networks: [Function: networks],
        address: [Object],
        links: [Function: links],
        events: [Function: events],
        binary: [Function: binary],
        deployedBinary: [Function: deployedBinary],
        unlinked_binary: [Object],
        bytecode: [Object],
        deployedBytecode: [Object],
        sourceMap: [Object],
        deployedSourceMap: [Object],
        source: [Object],
        sourcePath: [Object],
        ast: [Object],
        compiler: [Object],
        schema_version: [Function: schema_version],
        schemaVersion: [Function: schemaVersion],
        updated_at: [Function: updated_at],
        updatedAt: [Function: updatedAt] },
     _property_values: {},
     _json:
      { contractName: 'EncryptedToken',
        abi: [Array],
        bytecode: '0x60606040526301406f40600055341561001757600080fd5b600054600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061023e8061006c6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806370a0823114610051578063a9059cbb1461009e575b600080fd5b341561005c57600080fd5b610088600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100e0565b6040518082815260200191505060405180910390f35b34156100a957600080fd5b6100de600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610129565b005b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561017457fe5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555050505600a165627a7a723058205d5ccad9a01715b15d2733b795c870b25b04f5c79d10389a89ed6342e1e2b7ed0029',
        deployedBytecode: '0x60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806370a0823114610051578063a9059cbb1461009e575b600080fd5b341561005c57600080fd5b610088600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506100e0565b6040518082815260200191505060405180910390f35b34156100a957600080fd5b6100de600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610129565b005b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561017457fe5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555050505600a165627a7a723058205d5ccad9a01715b15d2733b795c870b25b04f5c79d10389a89ed6342e1e2b7ed0029',
        sourceMap: '26:518:0:-;;;79:8;54:33;;132:74;;;;;;;;187:14;;164:8;:20;173:10;164:20;;;;;;;;;;;;;;;:37;;;;26:518;;;;;;',
        deployedSourceMap: '26:518:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;446:96;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;245:165;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;446:96;499:7;521:8;:16;530:6;521:16;;;;;;;;;;;;;;;;514:23;;446:96;;;:::o;245:165::-;330:7;306:8;:20;315:10;306:20;;;;;;;;;;;;;;;;:31;;299:39;;;;;;368:7;344:8;:20;353:10;344:20;;;;;;;;;;;;;;;;:31;;;;;;;;;;;398:7;381:8;:13;390:3;381:13;;;;;;;;;;;;;;;;:24;;;;;;;;;;;245:165;;:::o',
        source: 'pragma solidity ^0.4.17;\n\ncontract EncryptedToken {\n  uint256 INITIAL_SUPPLY = 21000000;\n  mapping (address => uint256) balances;\n  function EncryptedToken() {\n    balances[msg.sender] = INITIAL_SUPPLY;\n  }\n\n  //转帐到一个指定的地址\n  function transfer(address _to, uint256 _amount) {\n    assert(balances[msg.sender] >= _amount);\n    balances[msg.sender] -= _amount;\n    balances[_to] += _amount;\n  }\n\n  //查看指定地址的余额\n  function balanceOf(address _owner) constant returns (uint256) {\n    return balances[_owner];\n  }\n}\n',
        sourcePath: '/home/sily/SmartContractDemo/HelloWorld/contracts/EncryptedToken.sol',
        ast: [Object],
        compiler: [Object],
        networks: [Object],
        schemaVersion: '1.0.1',
        updatedAt: '2018-01-11T08:57:36.557Z' },
     setProvider: [Function: bound setProvider],
     new: [Function: bound new],
     at: [Function: bound at],
     deployed: [Function: bound deployed],
     defaults: [Function: bound defaults],
     hasNetwork: [Function: bound hasNetwork],
     isDeployed: [Function: bound isDeployed],
     detectNetwork: [Function: bound detectNetwork],
     setNetwork: [Function: bound setNetwork],
     resetAddress: [Function: bound resetAddress],
     link: [Function: bound link],
     clone: [Function: bound clone],
     addProp: [Function: bound addProp],
     toJSON: [Function: bound toJSON],
     web3:
      Web3 {
        _requestManager: [RequestManager],
        currentProvider: [Provider],
        eth: [Eth],
        db: [DB],
        shh: [Shh],
        net: [Net],
        personal: [Personal],
        bzz: [Swarm],
        settings: [Settings],
        version: [Object],
        providers: [Object],
        _extend: [Function] },
     class_defaults:
      { from: '0x73b5b6e153e03ec11eeeb3d623e50867a5e5227e',
        gas: 6721975,
        gasPrice: 100000000000 },
     currentProvider:
      HttpProvider {
        host: 'http://localhost:8545',
        timeout: 0,
        user: undefined,
        password: undefined,
        send: [Function],
        sendAsync: [Function],
        _alreadyWrapped: true },
     network_id: '1515660380238' },
  abi:
   [ { constant: true,
       inputs: [Array],
       name: 'balanceOf',
       outputs: [Array],
       payable: false,
       stateMutability: 'view',
       type: 'function' },
     { constant: false,
       inputs: [Array],
       name: 'transfer',
       outputs: [],
       payable: false,
       stateMutability: 'nonpayable',
       type: 'function' },
     { inputs: [],
       payable: false,
       stateMutability: 'nonpayable',
       type: 'constructor' } ],
  contract:
   Contract {
     _eth:
      Eth {
        _requestManager: [RequestManager],
        getBalance: [Function],
        getStorageAt: [Function],
        getCode: [Function],
        getBlock: [Function],
        getUncle: [Function],
        getCompilers: [Function],
        getBlockTransactionCount: [Function],
        getBlockUncleCount: [Function],
        getTransaction: [Function],
        getTransactionFromBlock: [Function],
        getTransactionReceipt: [Function],
        getTransactionCount: [Function],
        call: [Function],
        estimateGas: [Function],
        sendRawTransaction: [Function],
        signTransaction: [Function],
        sendTransaction: [Function],
        sign: [Function],
        compile: [Object],
        submitWork: [Function],
        getWork: [Function],
        coinbase: [Getter],
        getCoinbase: [Function],
        mining: [Getter],
        getMining: [Function],
        hashrate: [Getter],
        getHashrate: [Function],
        syncing: [Getter],
        getSyncing: [Function],
        gasPrice: [Getter],
        getGasPrice: [Function],
        accounts: [Getter],
        getAccounts: [Function],
        blockNumber: [Getter],
        getBlockNumber: [Function],
        protocolVersion: [Getter],
        getProtocolVersion: [Function],
        iban: [Function],
        sendIBANTransaction: [Function: bound transfer] },
     transactionHash: null,
     address: '0xeff776cc1aa2f01c40e638538fdfaadbfb179b00',
     abi: [ [Object], [Object], [Object] ],
     balanceOf:
      { [Function: bound ]
        request: [Function: bound ],
        call: [Function: bound ],
        sendTransaction: [Function: bound ],
        estimateGas: [Function: bound ],
        getData: [Function: bound ],
        address: [Circular] },
     transfer:
      { [Function: bound ]
        request: [Function: bound ],
        call: [Function: bound ],
        sendTransaction: [Function: bound ],
        estimateGas: [Function: bound ],
        getData: [Function: bound ],
        'address,uint256': [Circular] },
     allEvents: [Function: bound ] },
  balanceOf:
   { [Function]
     call: [Function],
     sendTransaction: [Function],
     request: [Function: bound ],
     estimateGas: [Function] },
  transfer:
   { [Function]
     call: [Function],
     sendTransaction: [Function],
     request: [Function: bound ],
     estimateGas: [Function] },
  sendTransaction: [Function],
  send: [Function],
  allEvents: [Function: bound ],
  address: '0xeff776cc1aa2f01c40e638538fdfaadbfb179b00',
  transactionHash: null }

接下来,调用contract合约的balanceOf()函数和transfer()函数,验证结果正确。

truffle(development)> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 7, c: [ 21000000 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
truffle(development)> contract.trans
contract.transactionHash  contract.transfer         

truffle(development)> contract.transfer(web3.eth.accounts[1],100000000)
Error: VM Exception while processing transaction: invalid opcode
    at Object.InvalidResponse (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:41484:16)
    at /home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:329530:36
    at /home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:325200:9
    at XMLHttpRequest.request.onreadystatechange (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:328229:7)
    at XMLHttpRequestEventTarget.dispatchEvent (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:176415:18)
    at XMLHttpRequest._setReadyState (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:176705:12)
    at XMLHttpRequest._onHttpResponseEnd (/home/sily/.nvm/nvm/versions/node/v9.3.0/lib/node_modules/truffle/build/cli.bundled.js:176860:12)
truffle(development)> contract.transfer(web3.eth.accounts[1],100)
{ tx: '0x5de889a31a95a7971439c54659b91f95dce1103359527ad270cc8d507246c339',
  receipt:
   { transactionHash: '0x5de889a31a95a7971439c54659b91f95dce1103359527ad270cc8d507246c339',
     transactionIndex: 0,
     blockHash: '0x430cd8a197cd900e20374d14e20ad655fa82d456ca31e896c937e3f2cb6b9005',
     blockNumber: 12,
     gasUsed: 49056,
     cumulativeGasUsed: 49056,
     contractAddress: null,
     logs: [],
     status: 1 },
  logs: [] }
truffle(development)> contract.balanceOf(web3.eth.accounts[0])
BigNumber { s: 1, e: 7, c: [ 20999900 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 100 ] }
truffle(development)>

以上就是基于智能合约建立最简单的具有转账和查询钱包地址余额功能的代币教程。但是,逻辑中并没有对安全相关的操作进行处理,比如当余额不够时或者地址不合法等。

文章参考: http://blog.csdn.net/liyuechun520/article/details/78049950