5.2 智能合约实战 - FurnaceCoin

智能合约实战 - FurnaceCoin

本示例用于展示如何基于ERC20标准代币,实现一套自己的代币。

1. 创建项目和设置配置信息

1.1 创建项目 FurnaceCoin

mkdir FurnaceCoin
cd FurnaceCoin

1.2 配置truffle信息

truffle init

在生成的文件truffle.js配置以太坊网络等信息。

1.3 配置nodejs信息

npm init

在生成的文件package.json中配置依赖的npm包。

2. 编写智能合约

2.1 编写智能合约 FurnaceCoin

编写智能合约 contracts/FurnaceCoin.sol,实现自己的代币。

示例如下:

pragma solidity ^0.4.23;

import "../node_modules/zeppelin-solidity/contracts/ownership/Ownable.sol";
import "../node_modules/zeppelin-solidity/contracts/token/ERC20/StandardToken.sol";

contract FurnaceCoin is Ownable, StandardToken {
    string public name = "FurnaceCoin";
    string public symbol = "FCoin";
    uint public decimals = 18;
    uint public INITIAL_SUPPLY = 1000000000;

    constructor(uint _initSupply) public {
        if (_initSupply <= 0)
            _initSupply = INITIAL_SUPPLY;

        totalSupply_ = _initSupply;

        balances[msg.sender] = totalSupply_;

        emit Transfer(address(0x0), msg.sender, totalSupply_);
    }

    function mint(uint _value) public onlyOwner returns(bool) {
        totalSupply_ += _value;

        balances[msg.sender] += _value;

        emit Transfer(address(0x0), msg.sender, _value);
    }
}

这里用到了 zeppelin-solidity 库中的 ERC20 标准代币实现。

FurnaceCoin 在创建时便设定初始代币数量,并全部分配给创建者。
同时支持创建者在后续增加代币的总数量,通过方法 mint() 实现。

3. 部署智能合约

3.1 编写部署脚本

编写部署脚本 migrations/2_deploy_contracts.js。

示例如下:

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

module.exports = function(deployer) {
    var initSupply = 3000000000;
    
    deployer.deploy(FurnaceCoin, initSupply);
};

3.2 将智能合约部署到以太坊网络

truffle migrate --network ganache

这里,需要记住各智能合约在指定以太坊网络中的合约地址。

4. 使用 FurnaceCoin

这里只通过 nodejs 脚本来使用 FurnaceCoin,存放于目录 scripts。

4.1 转账操作

脚本 scripts/transfer.js 用于实现转账操作。

var Web3 = require('web3');
var contract = require('truffle-contract');

//var rpcUrl = "http://localhost:7545";
var rpcUrl = "http://192.168.60.12:7545";

const provider = new Web3.providers.HttpProvider(rpcUrl);
const web3 = new Web3(provider);

const FurnaceCoin = contract(require('../build/contracts/FurnaceCoin.json'));
FurnaceCoin.setProvider(provider);

const showError = error => {
    console.error(error);
};

var addressOfFurnaceCoin = "0xbefd2d3f950f0e948bd0bef1f03f84636185c09c";
var deployed = FurnaceCoin.at(addressOfFurnaceCoin);

var accounts = web3.eth.accounts;
var acc0, balanceOfAcc0;
var acc1, balanceOfAcc1;
var acc2, balanceOfAcc2;
var acc3, balanceOfAcc3;
var acc4, balanceOfAcc4;

if (accounts.length > 0)
    acc0 = web3.eth.accounts[0];

if (accounts.length > 1)
    acc1 = web3.eth.accounts[1];

if (accounts.length > 2)
    acc2 = web3.eth.accounts[2];

if (accounts.length > 3)
    acc3 = web3.eth.accounts[3];

if (accounts.length > 4)
    acc4 = web3.eth.accounts[4];

var async_func_transfer = async function(to, value, sender) {
    var result = await deployed.transfer(to, value, {from: sender});
    console.log("\nTest transfer (sender:", sender, ", to:", to, ", value:", value, ").");
    console.log("transfer tx: ", result.tx);
}

async_func_transfer(acc1, 1000, acc0);

4.2 监听事件

创建脚本 scripts/events.js 用于监听 FurnaceCoin 触发的日志。

scripts/events.js

var Web3 = require('web3');
var contract = require('truffle-contract');

//var rpcUrl = "http://localhost:7545";
var rpcUrl = "http://192.168.60.12:7545";

const provider = new Web3.providers.HttpProvider(rpcUrl);
const web3 = new Web3(provider);

const FurnaceCoin = contract(require('../build/contracts/FurnaceCoin.json'));
FurnaceCoin.setProvider(provider);

const showError = error => {
    console.error(error);
};

var addressOfFurnaceCoin = "0xbefd2d3f950f0e948bd0bef1f03f84636185c09c";
var deployed = FurnaceCoin.at(addressOfFurnaceCoin);

console.log("Event Lister.");

var eventTransfer = deployed.Transfer();
var eventApproval = deployed.Approval();

eventTransfer.watch(function(error, result){
    if (error) {
        console.error(error);
        return;
    }
    
    console.log('\nEvent for Transfer:');
    console.log('transactionIndex: ', result.transactionIndex);
    console.log('transactionHash: ', result.transactionHash);
    console.log('blockHash: ', result.blockHash);
    console.log('blockNumber: ', result.blockNumber);
    console.log('addressOfFurnaceCoin: ', result.address);
    console.log('from: ', result.args.from);
    console.log('to: ', result.args.to);
    console.log('value: ', result.args.value.toNumber());
});

eventApproval.watch(function(error, result){
    if (error) {
        console.error(error);
        return;
    }
    
    console.log('\nEvent for Approval:');
    console.log('transactionIndex: ', result.transactionIndex);
    console.log('transactionHash: ', result.transactionHash);
    console.log('blockHash: ', result.blockHash);
    console.log('blockNumber: ', result.blockNumber);
    console.log('addressOfFurnaceCoin: ', result.address);
    console.log('owner: ', result.args.owner);
    console.log('spender: ', result.args.spender);
    console.log('value: ', result.args.value.toNumber());
});

4.3 使用 FurnaceCoin

创建脚本 scritps/test.js,调用 FurnaceCoin 中的方法。

主要是:

  • 对于每个字段,调用同名的获取方法,查看对应值。
  • 通过方法 totalSupply() 查看总的代币数量。
  • 通过方法 balanceOf() 查询给定账户的代币余额。
  • 通过方法 transfer() 转账指定数量的代币给指定账户。
  • 通过方法 approve() 授权指定数量的代币可由指定账户进行支付。
  • 通过方法 allowance() 查询授权支付信息。
  • 通过方法 increaseApproval() 追加支付授权。
  • 通过方法 decreaseApproval() 撤销指定数量的支付授权。
  • 通过方法 transferFrom() 进行授权支付。

特别需要注意的是,当调用方法 transfer(), transferFrom() 时会触发事件 Transfer。
当调用方法 approve(), increaseApproval(), decreaseApproval() 时会触发事件 Approval。
启动脚本 scripts/events.js 将能够监听到这些日志事件,并输出日志的详细信息。

var Web3 = require('web3');
var contract = require('truffle-contract');

//var rpcUrl = "http://localhost:7545";
var rpcUrl = "http://192.168.60.12:7545";

const provider = new Web3.providers.HttpProvider(rpcUrl);
const web3 = new Web3(provider);

const FurnaceCoin = contract(require('../build/contracts/FurnaceCoin.json'));
FurnaceCoin.setProvider(provider);

const showError = error => {
    console.error(error);
};

var addressOfFurnaceCoin = "0xbefd2d3f950f0e948bd0bef1f03f84636185c09c";
var deployed = FurnaceCoin.at(addressOfFurnaceCoin);

var accounts = web3.eth.accounts;
var acc0, balanceOfAcc0;
var acc1, balanceOfAcc1;
var acc2, balanceOfAcc2;
var acc3, balanceOfAcc3;
var acc4, balanceOfAcc4;

if (accounts.length > 0)
    acc0 = web3.eth.accounts[0];

if (accounts.length > 1)
    acc1 = web3.eth.accounts[1];

if (accounts.length > 2)
    acc2 = web3.eth.accounts[2];

if (accounts.length > 3)
    acc3 = web3.eth.accounts[3];

if (accounts.length > 4)
    acc4 = web3.eth.accounts[4];

console.log("Test FurnaceCoin.");
deployed.then(function() {
    return deployed.name();
})
.then(function(result) {
    console.log("\nTest name:");
    console.log("name: ", result);

    return deployed.symbol();
})
.then(function(result) {
    console.log("\nTest symbol:");
    console.log("symbol: ", result);

    return deployed.decimals();
})
.then(function(result) {
    console.log("\nTest decimals:");
    console.log("decimals: ", result.toNumber());

    return deployed.INITIAL_SUPPLY();
})
.then(function(result) {
    console.log("\nTest INITIAL_SUPPLY:");
    console.log("INITIAL_SUPPLY: ", result.toNumber());

    return deployed.totalSupply();
})
.then(function(result) {
    console.log("\nTest totalSupply_:");
    console.log("totalSupply_: ", result.toNumber());

    return deployed.balanceOf(acc0);
})
.then(function(result) {
    console.log("\nTest balanceOfAcc0:");
    console.log("balanceOfAcc0: ", result.toNumber());

    balanceOfAcc0 = result.toNumber();

    return deployed.balanceOf(acc1);
})
.then(function(result) {
    console.log("\nTest balanceOfAcc1:");
    console.log("balanceOfAcc1: ", result.toNumber());

    balanceOfAcc1 = result.toNumber();

    return deployed.transfer(acc1, 1000, {from: acc0});
})
.then(function(result) {
    console.log("\nTest transfer:");
    console.log("transfer tx: ", result.tx);

    return deployed.balanceOf(acc0);
})
.then(function(result) {
    console.log("\nTest balanceOfAcc0:");
    console.log("balanceOfAcc0: ", result.toNumber());

    balanceOfAcc0 = result.toNumber();

    return deployed.balanceOf(acc1);
})
.then(function(result) {
    console.log("\nTest balanceOfAcc1:");
    console.log("balanceOfAcc1: ", result.toNumber());

    balanceOfAcc1 = result.toNumber();

    return deployed.approve(acc0, 3000, {from: acc0});
})
.then(function(result) {
    console.log("\nTest approve:");
    console.log("approve tx: ", result.tx);

    return deployed.allowance(acc0, acc0);
})
.then(function(result) {
    console.log("\nTest allowance:");
    console.log("allowance: ", result.toNumber());

    return deployed.increaseApproval(acc0, 1000, {from: acc0});
})
.then(function(result) {
    console.log("\nTest increaseApproval:");
    console.log("increaseApproval tx: ", result.tx);

    return deployed.allowance(acc0, acc0);
})
.then(function(result) {
    console.log("\nTest allowance:");
    console.log("allowance: ", result.toNumber());

    return deployed.decreaseApproval(acc0, 500, {from: acc0});
})
.then(function(result) {
    console.log("\nTest decreaseApproval:");
    console.log("decreaseApproval tx: ", result.tx);

    return deployed.allowance(acc0, acc0);
})
.then(function(result) {
    console.log("\nTest allowance:");
    console.log("allowance: ", result.toNumber());

    return deployed.transferFrom(acc0, acc1, 1000, {from: acc0});
})
.then(function(result) {
    console.log("\nTest transferFrom:");
    console.log("transferFrom tx: ", result.tx);

    return deployed.balanceOf(acc0);
})
.then(function(result) {
    console.log("\nTest balanceOfAcc0:");
    console.log("balanceOfAcc0: ", result.toNumber());

    balanceOfAcc0 = result.toNumber();

    return deployed.balanceOf(acc1);
})
.then(function(result) {
    console.log("\nTest balanceOfAcc1:");
    console.log("balanceOfAcc1: ", result.toNumber());

    balanceOfAcc1 = result.toNumber();
})
.catch(showError);

项目源代码

项目源代码已逐步上传到 Github,地址为 https://github.com/windstamp/dapp/tree/master/examples/FurnaceCoin

Contributor

  1. Windstamp, https://github.com/windstamp
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357