以太坊Solidity開發(fā)入門(ERC20發(fā)幣篇)

環(huán)境部署

  • 安裝 geth(以太坊客戶端)
  • 安裝 truffle(智能合約開發(fā)工具包,用于編譯與自動(dòng)部署)
  • 安裝 ganache-cli (簡(jiǎn)易的本地以太坊測(cè)試鏈服務(wù)器)

安裝命令

$ brew tap ethereum/ethereum
$ brew install ethereum
$ npm install -g truffle
$ npm install -g ganache-cli

這里只寫了mac os的geth安裝方法,其他平臺(tái)的geth安裝方法請(qǐng)參考https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum

  • 檢測(cè)是否安裝成功
  • 一切就緒后,會(huì)顯示各個(gè)工具的版本數(shù)據(jù)。
$ geth version //1.8.19-stable
$ truffle version // Truffle v4.1.14 (core: 4.1.14) 
$ ganache-cli --version //Ganache CLI v6.2.3 (ganache-core: 2.3.1)

項(xiàng)目構(gòu)建

  • 先新建一個(gè)文件夾,用truffle工具初始化,這里會(huì)自動(dòng)下載一些文件,如果網(wǎng)絡(luò)慢的話,需要等待一下。
$ mkdir erc20
$ cd erc20
$ truffle init
  • 初始化成功后,安裝openzeppelin-solidity模組,這是一套開源的智能合約代碼,里面已包括了其他大牛幫我們寫好了erc20的智能合約。
$ npm install openzeppelin-solidity
  • 在contracts目錄下新建MyToken.sol文件,并編輯。
  • 非常簡(jiǎn)單的,復(fù)制粘貼,就把ERC20的智能合約寫完了。
  • 其中TMD_TOKEN通證的名字,TMD是簡(jiǎn)稱(symbol),18是通證的精度,就是有18位小數(shù)的意思。
$ cd contracts
$ touch MyToken.sol
//MyToken.sol
pragma solidity ^0.4.24;
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";

contract MyToken is ERC20, ERC20Detailed {
    uint256 public constant INITIAL_SUPPLY = 10000 * (10 ** uint256(decimals()));
    constructor () public ERC20Detailed("TMD_TOKEN", "TMD", 18) {
        _mint(msg.sender, INITIAL_SUPPLY);
    }
}
  • 在migrations目錄下新建2_deploy_token.js,并編輯
  • 這個(gè)文件是對(duì)應(yīng)我們合約的文件,數(shù)字2代表第二個(gè)開始編譯部署。
$ cd migrations
$ touch 2_deploy_token.js
// 2_deploy_token.js
var MyToken = artifacts.require("./MyToken.sol");

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

  • 編輯truffle.js
module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // to customize your Truffle configuration!
  networks:{
    // 第一個(gè)是默認(rèn)的鏈接節(jié)點(diǎn),
    development:{
      host:'127.0.0.1',
      port:8545,
      network_id:'*'
    },
    //  rinkeby 測(cè)試網(wǎng)絡(luò)的設(shè)置,
    rinkeby: {
      host: "localhost",
      port: 8545,
      // 需要設(shè)置一個(gè)默認(rèn)的賬號(hào),錢包里可能有多個(gè)賬號(hào)。
      from: "0xBe255696870b84C69F6e2b902177Cf2a2cB57B58",
      network_id: 4,
      gas: 4700000,
    }
  },

  // 編譯配置
  solc:{
    optimizer:{
      enabled: true,
      runs: 200
    }
  }
};

啟動(dòng)測(cè)試節(jié)點(diǎn)

  • ganache-cli會(huì)啟動(dòng)一個(gè)本地的以太坊網(wǎng)絡(luò),里面有10個(gè)地址,并且每個(gè)地址都是有幣的。
  • geth attach鏈接區(qū)塊鏈會(huì)開啟一個(gè)繼承了web3.js的js運(yùn)行環(huán)境。
    啟動(dòng)一個(gè)新的窗口,連接到區(qū)塊鏈,查看一下數(shù)據(jù),如果默認(rèn)地址的錢不夠,部署會(huì)失敗。
$ ganache-cli
$ geth attach http://localhost:8545

// 展示錢包中的地址,在部署的時(shí)候,默認(rèn)會(huì)選中第一個(gè)。
> eth.accounts

// 查詢當(dāng)前默認(rèn)賬號(hào)余額,默認(rèn)情況下有 100000000000000000000 以太。
> eth.getBalance(eth.accounts[0])

部署erc20到本地私有鏈

  • 切換到項(xiàng)目目錄,也就是和truffle.js在同一層的目錄,先把智能合約編譯一次,
    編譯的時(shí)候會(huì)讀取該配置文件。
  • 編譯成功之后會(huì)生成一個(gè)build/contracts目錄,里面包含了一些編譯好的合約
  • 編譯成功后,查一下erc20的賬戶余額,確認(rèn)部署成功。
// 確認(rèn)一下項(xiàng)目地址。在 ./erc20 目錄下
$ pwd

// 如果編譯成功,會(huì)顯示 Writing artifacts to ./build/contracts
$ truffle compile

// --reset參數(shù)只是確保每次部署都是新的
// 如果部署成功,會(huì)顯示Saving successful migration to network...
// 并且顯示MyToken: 0x984ed7dfbb2926521c8d31de273295c1def894e4 這個(gè)合約的所在地址
$ truffle migrage --reset
  • 在剛才打開的 geth attach 的窗口檢測(cè)一下部署情況。
// 構(gòu)建一個(gè)簡(jiǎn)答的abi,只有查詢余額與精度
> var abi=[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"}];
> var contract = web3.eth.contract(abi).at(tokenAddress);
// 現(xiàn)在的balance是帶了精度了的,需要除以精度,才是正確的數(shù)值。
> var balance = contract.balanceOf('0x09d4a5310d25dec940d736872b01be718db5e724');
// 獲取精度
> var decimals = contract.decimals()

// 計(jì)算實(shí)際余額, 返回 10000,與我們?cè)O(shè)置的一樣,說(shuō)明erc20在私有網(wǎng)絡(luò)已經(jīng)部署成功
> balance.div(Math.pow(10, decimals));

部署erc20到公測(cè)鏈(rinkeby)

  • 導(dǎo)入/生成錢包文件到geth客戶端中,需要注意的是,測(cè)試網(wǎng)和正式網(wǎng)賬號(hào)是存儲(chǔ)在不同目錄的,會(huì)根據(jù)網(wǎng)絡(luò)參數(shù)寫入不同的文件。
  • 給賬號(hào)充值(在rinkeby測(cè)試網(wǎng)絡(luò)上)
  • 我已經(jīng)有一個(gè)含有測(cè)試幣的地址了,現(xiàn)在只要導(dǎo)入就好。
$ vim privatekey.txt
//貼入 056ef7c6a165f877a5aedb3cfe24b2bbcdd6c680d12df9a82092705fc03ce37f

//  --rinkeby 參數(shù)說(shuō)明是rinkeby測(cè)試網(wǎng)的文件。如果不帶參數(shù)則是正式網(wǎng)絡(luò)
$ geth --rinkeby  account import ./privatekey.txt
// 輸入密碼 12345678
// 重復(fù)密碼 12345678
// 展示 Address: {be255696870b84c69f6e2b902177cf2a2cb57b58},表示導(dǎo)入成功
$ rm privatekey.txt

// 查看一下當(dāng)前客戶端有那些地址,be255696870b84c69f6e2b902177cf2a2cb57b58也在其中。
$ geth --rinkeby account list

  • 啟動(dòng)客戶端,等待區(qū)塊信息同步完成,這一步可能會(huì)耗費(fèi)很長(zhǎng)時(shí)間。
  • 解鎖部署賬戶
$ geth  --rinkeby --rpc --rpcapi db,eth,net,web3,personal  
$ geth attach /Users/tmd/Library/Ethereum/rinkeby/geth.ipc

> personal.unlockAccount('0xbe255696870b84c69f6e2b902177cf2a2cb57b58')
// 輸入剛才設(shè)置但錢包密碼12345678

// 切換窗口
// 部署合約到公測(cè)網(wǎng),該配置在 truffle.js 文件中的rinkeby設(shè)置里。
$ truffle migrate --network rinkeby --reset

// 如果部署成功,會(huì)顯示與本地私有網(wǎng)顯示的類似。
// 如果部署不成功,很有可能是客戶端還沒(méi)同步完成,影響部署。

// 切換到 geth attach 窗口

// 在客客戶端查詢一下自己錢包的余額
> eth.getBalance('0xbe255696870b84c69f6e2b902177cf2a2cb57b58')
// 余額顯示為0,但是這個(gè)地址在測(cè)試網(wǎng)是有錢的,說(shuō)明我目前的節(jié)點(diǎn)同步未完成,無(wú)法進(jìn)行部署,需要等待區(qū)塊同步結(jié)束

通過(guò)ether.js部署ERC20

  • ether.js是一個(gè)nodejs與以太坊交互的工具,如果不熟悉可以看看我上一篇文章
  • 由于已經(jīng)通過(guò)truffle compile命令編譯了合約文件,現(xiàn)在只要讀取合約文件中的bytecodeabi部署到區(qū)塊網(wǎng)絡(luò)即可,不需要經(jīng)過(guò)等待漫長(zhǎng)的客戶端區(qū)塊同步了
$ npm install ether
//ERC20.js
const ethers = require('ethers');
const fs = require('fs');
const util = require('util');
const readFile =util.promisify(fs.readFile);

const provider = ethers.getDefaultProvider('rinkeby');
const mnemonic1 = 'utility opinion husband upset finger side round exhaust arm allow pilot hospital';
const _wallet = ethers.Wallet.fromMnemonic(mnemonic1);
const wallet = _wallet.connect(provider);

const createERC20 = async () => {
  // 讀取編譯完成的文件。
  const file = await readFile('./erc20/build/contracts/MyToken.json',{encoding: 'utf8'})  ;
  const obj = JSON.parse(file);
  const abi = obj.abi;
  const bytecode= obj.bytecode;
  // console.log(abi);
  // console.log(bytecode);
  let factory = new ethers.ContractFactory(abi, bytecode, wallet);
  let contract = await factory.deploy();
  return contract.deployed()
};
createcreateToken().then(result=>console.log('result: ',result)).catch(err=>console.log('err: ',err));


  • 運(yùn)行后如果顯示一大串信息,并且有交易哈希,則表示運(yùn)行成功
  • 運(yùn)行結(jié)果中,找到合約地址為0x13f60906DE3758F025cdA95899d3742DC60C24A4

ERC20 轉(zhuǎn)賬

  • 轉(zhuǎn)賬的時(shí)候一定要先讀取精度,雖然這個(gè)函數(shù)不是一定有的,但是大多數(shù)erc20都提供了該接口。
const transferERC20 = async () => {
  const file = await readFile('./erc20/build/contracts/MyToken.json',{encoding: 'utf8'})  ;
  const obj = JSON.parse(file);
  const abi = obj.abi;
  const contractAddress = '0x13f60906DE3758F025cdA95899d3742DC60C24A4';
  const contract = new ethers.Contract(contractAddress, abi, provider);
  const decimals = await contract.decimals();
  const name = await contract.name();
  const totalSupply =  await contract.totalSupply();
  const symbol = await contract.symbol();
  console.log('name: ',name);
  console.log('symbol: ',symbol) ;
  console.log('decimals: ',decimals) ;
  console.log('totalSupply: ',ethers.utils.formatUnits(totalSupply, decimals)) ;
  
  // 合約簽名
  const contractWithSigner = contract.connect(wallet);
  const amount = '10';
  // 數(shù)字序列化
  const numberOfTokens = ethers.utils.parseUnits(amount, decimals);
  // 發(fā)起轉(zhuǎn)賬
  const tx = await contractWithSigner.transfer('0xbe79D5B66A5D44607F91E312ec5E35b8c92db5bf', numberOfTokens);
  await tx.wait();
  console.log('hash',tx.hash);

  // 查詢余額
  const _balance = await contract.balanceOf('0xbe79D5B66A5D44607F91E312ec5E35b8c92db5bf');
  // 數(shù)字反序列化
  const balance = ethers.utils.formatUnits(_balance, decimals);
  console.log('balance: ',balance, symbol)

};


transferERC20().catch(console.log);
  • 結(jié)果顯示余額與symbol,說(shuō)明轉(zhuǎn)賬成功。
  • 因?yàn)樵摵霞s是部署在公測(cè)網(wǎng)上的,可以通過(guò)區(qū)塊瀏覽器查詢到該合約交易歷史
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容