区块链100讲:今天想要教大家发个币~

Posted by

区块链100讲:今天想要教大家发个币~

本文作者:HiBlock区块链技术布道群-胡键

原文发布于简书

原文链接:

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

加微信baobaotalk_com,加入技术布道群

区块链100讲:今天想要教大家发个币~

本讲将通过一些简单的例子从近处看以太坊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 <action> [from] access simple-data contract from an

[value] external address.

app.js myico <command> [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 <action> [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, {from: argv.from}).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 <action> [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 <command> [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({from: argv.purchaser, value: web3.utils.toWei(argv.value.toString(), “ether”)})

.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,给账户发行代币

到这里,相信大家应该不会再觉得“发币”有什么神秘的了,接下来如何将其应用到业务中,作为业务的催化剂(而不是割韭菜利器)就靠各位的想象力了。(注:本文只分享技术,不做任何项目及投资建议)

— 线上课程推荐–

区块链100讲:今天想要教大家发个币~

始发于微信公众号: 区块链社区HiBlock