當前位置:
首頁 > 最新 > 區塊鏈100講:今天想要教大家發個幣

區塊鏈100講:今天想要教大家發個幣

本文作者:HiBlock區塊鏈技術佈道群-胡鍵

原文發佈於簡書

https://www.jianshu.com/p/d78353772029

本講將通過一些簡單的例子從近處看以太坊DApp的開發細節。偷偷告訴你,本文會涉及到以太坊中的一個熱門場景:「發幣」,滿足一下各位苦逼的開發當一回大佬的願望 ;)

1

背景

本文用到的開發工具:

Node

Truffle

相關的包:

yargs,cli庫

web3,json-rpc抽象

truffle-contract,合約抽象

openzeppelin-solidity,安全合約庫

文章中創建的項目為一個「node + truffle」工程,對外提供cli。這個cli暴露了兩條命令:

$./app.js help

app.js [命令]

命令:

app.js simple-data [from] access simple-data contract from an

[value] external address.

app.js myico [purchaser] commands about my ico.

[value]

選項:

--version 顯示版本號 [布爾]

--help 顯示幫助信息 [布爾]

選擇以cli而非gui的方式作為dapp的前端主要的理由:

當前的重點是迅速掌握以太坊dapp開發的套路。

cli相比起gui來講,省去了很多麻煩事。而且,我對於gui的開發,實在興趣不大。

2

準備

那麼,讓我們先來準備工程的架子:

mkdir 目錄 && cd 目錄

npm init

truffle init

npm install yargs --save

執行完成後,cli工程需要基本環境就都具備了。之後,在項目的工程根下創建app.js,它將作為整個工程的入口。並且工程採用Command Module(https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module)的方式組織。

app.js的內容如下:

#!/usr/bin/env node

require("yargs")

.command(require("./simple-data.js"))

.command(require("./myico.js"))

.help()

.argv

其中的兩條命令以module方式組織,分別對應:

simple-data,簡單合約交互

my i-c-o,i-c-o合約交互

3

simple-data

simple-data命令展示了一個簡單的前端和合約交互的例子,為編寫複雜交互提供了參考。它的整個過程如下:

(1)npm install web3 --save

(2)npm install truffle-contract --save

(3)編寫合約

pragma solidity ^0.4.23;

contract SimpleData {

address public owner;

uint data;

constructor() public {

owner = msg.sender;

}

function set(uint x) public{

data = x;

}

function get() view public returns (uint) {

return data;

}

}

(4)編寫Migration文件

const SimpleData = artifacts.require("./SimpleData.sol");

module.exports = function(deployer) {

deployer.deploy(SimpleData);

};

(5)編寫Command

const Web3 = require("web3");

const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:9545"));

const contract = require("truffle-contract");

const SimpleDataContract = require("./build/contracts/SimpleData.json");

const simpleData = contract(SimpleDataContract);

simpleData.setProvider(web3.currentProvider);

if (typeof simpleData.currentProvider.sendAsync !== "function") {

simpleData.currentProvider.sendAsync = function() {

return simpleData.currentProvider.send.apply(

simpleData.currentProvider, arguments

);

};

}

exports.command = "simple-data [from] [value]";

exports.describe = "access simple-data contract from an

external address.";exports.handler = function(argv) {

if(argv.action == "get") {

simpleData.deployed().then(function(instance){

instance.get().then(function(result){

console.log(+result);

})

});

} else if(argv.action == "set") {

if(!argv.value) {

console.log("No value provided!");

return;

}

simpleData.deployed().then(function(instance){

instance.set(argv.value, ).then(function(result){

console.log(result);

})

});

} else {

console.log("Unknown action!");

}

}

說明:

「http://localhost:9545」對應「truffle develop」環境中的埠。

「./build/contracts/SimpleData.json」由「truffle compile」產生,這個json文件將和truffle-contract一起配合產生針對於合約的抽象。

「if(typeof ... !== "function") { ... }」這個if block是對於truffle-contract這個issue的walkaround。

隨後的exports則是yargs command module的介面要求。對於命令:「simple-data [from] [value]」,其格式由yargs解析:,必填;[...],選填

編譯部署之後,就可以簡單的試用了(先給app.js可執行許可權):

app.js simple-data get

app.js simple-data set 地址 值

4

my--i-c-o

接下來,就到了最讓人期待的時刻:發幣,更準確的說是基於ERC 20的代幣發放。因為以太坊中各個幣種有不同的含義和用途,比如另一個常見的幣種:ERC 721,它發行的每個token都是獨一無二不可互換的,比如以太貓。

關於發幣,究其本質就是實現特定的合約介面。從開發的投入產出比來講,我建議採用OpenZeppelin:

跟錢打交道的事情需要慎重,由不安全合約導致的問題屢見不鮮。

自己編寫安全合約並不簡單。

OpenZeppelin包含了當前安全合約的最簡實踐,其背後的公司本身就提供安全審計服務。

可復用合約代碼加快了合約的開發。

以下是使用OpenZeppelin開發i-c-o的過程:

(1)npm install -E openzeppelin-solidity

(2)i-c-o的過程由兩個合約組成,它們都將直接基於OpenZeppelin的合約完成。

coin,代幣

crowdsale,眾籌

(3)代幣合約

pragma solidity ^0.4.23;

import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol";

contract MyCoin is MintableToken {

string public name = "MY COIN"; // 代幣名稱

string public symbol = "MYC"; // 代幣代碼

uint8 public decimal = 18; // 位數

}

(4)眾籌合約

pragma solidity ^0.4.23;

import "../node_modules/openzeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol";

import "../node_modules/openzeppelin-solidity/contracts/crowdsale/validation/TimedCrowdsale.sol";

contract MyCrowdsale is TimedCrowdsale, MintedCrowdsale {

constructor (

uint256 _openingTime,

uint256 _closingTime,

uint256 _rate,

address _wallet,

MintableToken _token

)

public

Crowdsale(_rate, _wallet, _token)

TimedCrowdsale(_openingTime, _closingTime) {

}

}

幾行代碼就完成了核心合約的開發,這要歸功於咱們選的框架,;)

(5)Migration腳本

const MyCoin = artifacts.require("./MyCoin.sol");

const MyCrowdsale = artifacts.require("./MyCrowdsale.sol");

module.exports = function(deployer, network, accounts) {

const openingTime = web3.eth.getBlock("latest").timestamp + 2;

const closingTime = openingTime + 3600;

const rate = new web3.BigNumber(1000);

const wallet = accounts[1];

deployer.deploy(MyCoin).then(function() {

return deployer.deploy(MyCrowdsale, openingTime, closingTime, rate, wallet, MyCoin.address);

}).then(function() {

return MyCoin.deployed();

}).then(function(instance) {

var coin = instance;

coin.transferOwnership(MyCrowdsale.address);

});

};

說明:

上面的合約定義很清楚地表明,眾籌需要有Token的地址,因此眾籌合約需要在Token部署成功之後進行。

眾籌合約需要得到Token的所有權才能進行發行。一開始,Token的owner是部署者(這裡是account[0]),因此在眾籌合約部署完成之後需要完成Token所有權的移交。

最後一步非常關鍵,否則會出現類似下面的錯誤:

Error: VM Exception while processing transaction: revert

at Object.InvalidResponse ...

...

(6)最後,就是my i-c-o的命令編寫了。

const Web3 = require("web3");

const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:9545"));

const contract = require("truffle-contract");

const MyCoin = require("./build/contracts/MyCoin.json");

const myCoin = contract(MyCoin);

myCoin.setProvider(web3.currentProvider);

if (typeof myCoin.currentProvider.sendAsync !== "function") {

myCoin.currentProvider.sendAsync = function() {

return myCoin.currentProvider.send.apply(

myCoin.currentProvider, arguments

);

};

}

const MyCrowdsale = require("./build/contracts/MyCrowdsale.json");

const myCrowdsale = contract(MyCrowdsale);

myCrowdsale.setProvider(web3.currentProvider);

if (typeof myCrowdsale.currentProvider.sendAsync !== "function") {

myCrowdsale.currentProvider.sendAsync = function() {

return myCrowdsale.currentProvider.send.apply(

myCrowdsale.currentProvider, arguments

);

};

}

exports.command = "myico [purchaser] [value]";

exports.describe = "commands about my ico.";

exports.handler = function(argv) {

if(argv.command == "hasClosed") {

myCrowdsale.deployed().then(function(instance){

instance.hasClosed().then(function(result){

console.log(result);

});

});

} else if(argv.command == "deliver") {

myCrowdsale.deployed().then(function(instance){

instance.token().then(function(address){

instance.sendTransaction()

.then(function(result){

console.log("done!");

}).catch(function(error){

console.error(error);

});

});

});

} else if(argv.command == "totalSupply") {

myCrowdsale.deployed().then(function(instance){

instance.token().then(function(address){

let coinInstance = myCoin.at(address);

coinInstance.totalSupply().then(function(result){

console.log(+result);

});

});

});

} else if(argv.command == "balance") {

myCrowdsale.deployed().then(function(instance){

instance.token().then(function(address){

let coinInstance = myCoin.at(address);

coinInstance.balanceOf(argv.purchaser).then(function(balance){

console.log(+balance);

});

});

});

} else {

console.log("Unknown command!");

}

}

有了前面simple-data命令代碼的說明,這裡的代碼應該不會有任何難點了。其中的子命令:

hasClosed,眾籌是否結束

totalSupply,眾籌中產生的token總量

balance,某個賬戶的代幣數

deliver,給賬戶發行代幣

到這裡,相信大家應該不會再覺得「發幣」有什麼神秘的了,接下來如何將其應用到業務中,作為業務的催化劑(而不是割韭菜利器)就靠各位的想像力了。(註:本文只分享技術,不做任何項目及投資建議)

-- 線上課程推薦--


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 區塊鏈社區HiBlock 的精彩文章:

TAG:區塊鏈社區HiBlock |