以太坊与IPFS

在私有链上发布代币

2019-01-31  本文已影响0人  白昔月

前言

truffle

本文参考了https://eips.ethereum.org/EIPS/eip-20https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md这两篇文章,这也是官方发布的erc20代币的标准文章。如果我有写的不明白的地方,请大家直接查看官方文档。当然,本文还介绍了如何使用truffle来编译和部署合约。

代币开发

工具概述

开发代币需要使用如下工具Solidity、Remix(或者Truffle + VSCode,VScode是我自己比较喜欢使用的,但是,在开发solidity的时候真的不怎么好使,大家可以使用其他的IDE)、MetaMask。Solidity是EVM的编程语言,Remix(http://remix.ethereum.org/)是IDE(Turffle是一个编译环境,还可以部署合约看大家喜好,本文后边会重点讲解truffle),MetaMask是以太坊钱包(也可以使用其他钱包,反正只要是可以反映自己的货币数量的工具即可)

注意事项

发行一个代币很容易,但是发行一个符合erc20标准的代币就有约束了,我们在注意事项中,将这些约束写一下。

1.solidity的版本必须高于^0.4.17
2.调用方必须处理返回的false(bool success),调用方不能假定永远不会返回false
3.必须保证构建的token可以兼容其他任何交易所和钱包

接口说明

在原文档中,有些接口是可选实现的,但是,为了提高一款代币的可用性,我就将我觉得需要实现的接口都展示在下方了:

接口 说明
name 代币全称,function name() public view returns (string)
symbol 代币简称,function symbol() public view returns (string)
decimals 代币精度,function decimals() public view returns (uint8)
totalSupply 获取发行的代币总数,function totalSupply() public view returns (uint256)
balanceOf 查询账户余额,function balanceOf(address _owner) public view returns (uint256 balance)
transfer 代币转账,发送Token到某个地址(转账),注意,0值转移也是正常的业务需求,也将会出发该方法,只有余额不足的情况下,才会报错。function transfer(address _to, uint256 _value) public returns (bool success)
transferFrom 从地址from发送token到to地址,与transfer相比,该方法多了一个发送方的参数,该函数其他功能与transfer基本类似。function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
approve 允许_spender从你的账户转出token,function approve(address _spender, uint256 _value) public returns (bool success)
allowance 查询允许spender转移的Token数量,function allowance(address _owner, address _spender) public view returns (uint256 remaining)

事件说明

事件 说明
Transfer transfer方法调用时的通知事件,当转币发生的时候,提供给其他程序的监听,event Transfer(address indexed _from, address indexed _to, uint256 _value)
Approval approve方法调用时的通知事件,委托行为发生时,通过给其他程序的监听,event Approval(address indexed _owner, address indexed _spender, uint256 _value)

最佳实践

在基本原理弄明白之后,我们就要来实现具体的代码了。在erc20的官方文档中,他们提供了两个可以参考的已经实现的代币源码, OpenZeppelin implementationConsenSys implementation

我们此处就以OpenZeppelin implementation为例为大家讲解一下,如何快速完成代币开发。

OpenZeppelin

OpenZeppelin是一家提供合约审查、安全的公司,再总结个许多代币所存在的问题后,他们开发了这个开源代码源码。使用OpenZeppelin提供的开源代币源码,可以节省我们的大量时间。

准备

eth环境

我准备的是geth,当然大家也可以安装ganache-cli或者使用truffle-dev环境。

安装Truffle

npm i -g truffle
....
....
+ truffle@5.0.3
added 91 packages from 305 contributors in 14.517s
root@tumtest:~# truffle version
Truffle v5.0.3 (core: 5.0.3)
Solidity v0.5.0 (solc-js)

构建truffle box编译环境

truffle box提供了开箱即用的solidity开发、编译、部署环境。

mkdir TutorialToken
cd TutorialToken
truffle unbox tutorialtoken

✔ Preparing to download
✔ Downloading
✔ Cleaning up temporary files
✔ Setting up box

Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
  Run dev server: npm run dev

安装OpenZeppelin

此处需要使用OpenZeppelin的StandardToken.sol合约,或者说要扩展这个合约。我们先安装OpenZeppelin

npm i zeppelin-solidity

创建代币合约

我们定义的代币合约的名字是TutorialToken,当然,这个名字和目录名没有必然的联系。但是尽量还是保持合约名、目录名、构造函数名统一吧。

我们在/contracts目录下创建TutorialToken.sol智能合约。在此处说明一下,如果有需要定义图标的,需要与imtoken等钱包程序进行沟通与部署,合约中是不建议添加图标的(按字节收费)

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

contract TutorialToken is StandardToken {

  string public name = 'TutorialToken';
  string public symbol = 'TT';
  uint public decimals = 2;
  uint public INITIAL_SUPPLY = 100000;

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

添加部署文件

在/migrations目录下,创建文件2_deploy_contracts.js。另外,这个文件夹下还存在一个1_initial_migration.js文件,这个文件是定义合约owner的。2_deploy_contracts.js文件内容如下:

var TutorialToken = artifacts.require("./TutorialToken.sol");
  
module.exports = function(deployer) {
          deployer.deploy(TutorialToken);
};

调整配置文件

在truffle官方文档(https://truffleframework.com/docs/truffle/reference/configuration)说明要将truffle.js转换为truffle-config.js。因此,首先做这样的变换。另外,由于我的truffle对应的默认solidity版本是0.5.0,因此,需要修改版本为0.4.24(OpenZeppelin的代币合约都是使用这个版本的,不修改的话,我这个版本的solidity编译会报错)。配置文件修改完之后如下:

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // for more about customizing your Truffle configuration!
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*" // Match any network id
    }
  },
  compilers: {
          solc: {
           version: "0.4.24"
                }
            }
};

注意:此处如果不加版本号0.4.24的话,会报错SyntaxError: Source file requires different compiler version (current compiler is 0.5.0+commit.1d4f565a.Emscripten.clang - note that nightly builds are considered to be strictly less than the released version

编译与部署

部署前记得要设置好部署合约的账户,另外,还要解锁账户我自己的是使用矿工来发布,命令如下:personal.unlockAccount(web3.eth.accounts[0])

truffle compile
truffle migrate
⚠️  Important ⚠️ 
If you're using an HDWalletProvider, it must be Web3 1.0 enabled or your migration will hang.


Starting migrations...
======================
> Network name:    'development'
> Network id:      *
> Block gas limit: 8000000


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x178eb072152aedb1a3f6ce7a6d80422c9beaf1e4d2e124da9b459e020b27f3aa
   > Blocks: 0            Seconds: 0
   > contract address:    0x1c35b93DEaD37c25FeB096d8bA2Ac0338e2ef705
   > account:             0x0d221f5da9da6C4D2F1AB8030482Ae5FAB004975
   > balance:             6643
   > gas used:            277462
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00554924 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00554924 ETH


2_deploy_contracts.js
=====================

   Deploying 'TutorialToken'
   -------------------------
   > transaction hash:    0x6856e4dc961507e7e8f2b36672888c47259d3e369c4bfb11b99a899fa9928edf
   > Blocks: 4            Seconds: 4
   > contract address:    0x7A599aBF7912b910851E75E64e9d3f36025140f8
   > account:             0x0d221f5da9da6C4D2F1AB8030482Ae5FAB004975
   > balance:             6673
   > gas used:            1505388
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.03010776 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.03010776 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.035657 ETH


其他说明

truffle提供了一个node的服务(在truffle的文件夹下执行npm run dev),大家可以使用这个服务查询合约。但是,我认为此处应该简单说明一下web3的原理,因此,我把web3如何实现查询合约的功能写在下方:

.....
...
..
const ABI = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}];

const Contract = eth.contract(ABI);
....
..
var Token = Contract.at(contractAddress);
var actualBalance = eth.getBalance(contractAddress);
var totalSupply = Token.totalSupply();
var decimals = Token.decimals();
var name = Token.name();
var symbol = Token.symbol();

此处最重要的是构建ABI。

与ERC20合约交互

truffle console
TutorialToken.deployed().then(instance => contract = instance)
contract.balanceOf('0x0d221f5da9da6C4D2F1AB8030482Ae5FAB004975')
contract.transfer('0x4e0567221aa010636357d2a1a747c3e3723bd9fb', 150000)
//对于有一定精度的转账,可用通过乘法进行
contract.transfer('0x4e0567221aa010636357d2a1a747c3e3723bd9fb', 1000000*2000)

infrua + truffle

eth的infrua提供了节点数据,不需要自己跑节点了。我们可用到infrua官网申请infrua的api。另外,我们还要安装一个npm i truffle-hdwallet-provider,来管理自己的助记词相关的服务。如果报错了,就先安装个rpc模块npm install ethereumjs-testrpc

最后还要修改truffle.js文件

const HDWalletProvider = require('truffle-hdwallet-provider')
const mnemonic = '333 333 333 333 333 333 333 333 333 333 333 333'
const infura_apikey = '请更换为你自己的apikey'

module.exports = {
    networks: {
        development: {
            host: "127.0.0.1"
            , port: 7545
            , network_id: "*"
        }
        , private: {
            host: "localhost"
            , port: 8545
            , network_id: "*"
        }
        , ropsten: {
            provider: new HDWalletProvider(mnemonic, "https://ropsten.infura.io/" + infura_apikey)
            , network_id: 3
            , gas: 301238
            , gasPrice: 30000000000
        }, main: {
            provider: new HDWalletProvider(mnemonic, "https://mainnet.infura.io/" + infura_apikey)
            , network_id: 1
            , gas: 3012388
            , gasPrice: 1000000000
        }
    }
}

部署到各个网络的命令

truffle migrate --network development
truffle migrate --network private
truffle migrate --network ropsten
truffle migrate --network main

总结

本文介绍了构建一个满足erc20合约的代币需要做什么事情以及注意事项。并通过使用最佳实践的方式快速的开发了一个安全而又满足erc20标准的代币。我们可以结合web3对合约进行查询。

上一篇下一篇

猜你喜欢

热点阅读