Home | 简体中文 | 繁体中文 | 杂文 | 知乎专栏 | Github | OSChina 博客 | 云社区 | 云栖社区 | Facebook | Linkedin | 视频教程 | 打赏(Donations) | About
知乎专栏多维度架构微信号 netkiller-ebook | QQ群:128659835 请注明“读者”

16.7. 智能合约

16.7.1. 部署合约

解锁账号

			
> personal.unlockAccount(eth.accounts[0], "netkiller",5000)
true
> miner.stop()
true
> miner.start(2)			
			
			

编译只能合约

			
solc --bin --abi --optimize -o ./output helloworld.sol		
			
			
			
var Web3 = require("web3");
var fs = require ('fs');

var web3 = new Web3("http://localhost:8545");

var abi = JSON.parse(fs.readFileSync('output/Netkiller.abi'));
var bin = '0x'+fs.readFileSync('output/Netkiller.bin').toString();

var myContract = new web3.eth.Contract(abi, null, {
    from: '0x5c18a33DF2cc41a1bedDC91133b8422e89f041B7'
});

myContract.deploy({ data: bin }).send({
    from: '0x5c18a33DF2cc41a1bedDC91133b8422e89f041B7',
    gas: 1500000,
    gasPrice: '30000000000000'
}).then(function(newContractInstance){
    console.log(newContractInstance.options.address) 
});			
			
			

16.7.2. 使用最佳手续费创建合约

			
var fs = require('fs');
var net = require('net');
var Web3 = require("web3");
var fs = require ('fs');

var web3 = new Web3("http://localhost:8545");

var abi = JSON.parse(fs.readFileSync('NetkillerAdvancedToken.abi'));
var bin = '0x'+fs.readFileSync('NetkillerAdvancedToken.bin').toString();
var address = '0x22c57F0537414FD95b9f0f08f1E51d8b96F14029';
var myContract = new web3.eth.Contract(abi, null, {from: address});

var options = { data: bin, arguments: [100000000, 'Netkiller Test Coin', 'NTC', 18] };

web3.eth.getGasPrice().then(function(gasPrice){
    myContract.deploy(options).estimateGas(function(err, gas){
        console.log(gas);
        web3.eth.personal.unlockAccount(req.body.from, req.body.password).then(function(error){
            myContract.deploy(options).send({
                from: address,
                gas: gas,
                gasPrice: gasPrice
            }).then(function(newContractInstance){
                console.log(newContractInstance.options.address) 
            });
        });
    });
});			
			
			

16.7.3. 调用合约

部署智能合约,你可以使用钱包部署,也可以使用Truffer部署,不管你采用什么方式,最终我们需要合约地址。

			
pragma solidity ^0.4.18;

contract MetaCoin {
	mapping (address => uint) balances;

	event Transfer(address indexed _from, address indexed _to, uint256 _value);

	function MetaCoin() public {
		balances[tx.origin] = 10000;
	}

	function sendCoin(address receiver, uint amount) public returns(bool sufficient) {
		if (balances[msg.sender] < amount) return false;
		balances[msg.sender] -= amount;
		balances[receiver] += amount;
		Transfer(msg.sender, receiver, amount);
		return true;
	}

	function getBalanceInEth(address addr) public view returns(uint){
		return convert(getBalance(addr),2);
	}

	function getBalance(address addr) public view returns(uint) {
		return balances[addr];
	}
	
	function convert(uint amount,uint conversionRate) public pure returns (uint convertedAmount)
	{
		return amount * conversionRate;
	}
	
}

			
			

这里使用 Truffle 部署

			
neo@MacBook-Pro ~/ethereum/truffle % truffle compile --all
Compiling ./contracts/MetaCoin.sol...
Writing artifacts to ./build/contracts

neo@MacBook-Pro ~/ethereum/truffle % truffle migrate --reset
Using network 'development'.

Running migration: 5_initial_migration.js
  Deploying MetaCoin...
  ... 0x9c006b398733a1d8679cbb00493ca75ff063f51c34521ae67a70523deeebf9c4
  MetaCoin: 0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6
Saving successful migration to network...
  ... 0x755a48ef99e488d7cf8460d718773a5afe73f760fb87697e51c40f3e6086f10b
Saving artifacts...
			
			
			

得到合约地址 MetaCoin: 0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6

编译合约获得 abi 接口

			
neo@MacBook-Pro ~/ethereum/truffle % solc --bin --abi --optimize -o ./output contracts/MetaCoin.sol 
neo@MacBook-Pro ~/ethereum/truffle % cat output/MetaCoin.abi 
[{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getBalanceInEth","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"sendCoin","outputs":[{"name":"sufficient","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"amount","type":"uint256"},{"name":"conversionRate","type":"uint256"}],"name":"convert","outputs":[{"name":"convertedAmount","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"}]			
			
			
			
var Web3 = require("web3");
// 创建web3对象
var web3 = new Web3("http://localhost:9545");
// 合约ABI
var abi = [{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getBalanceInEth","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"sendCoin","outputs":[{"name":"sufficient","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"amount","type":"uint256"},{"name":"conversionRate","type":"uint256"}],"name":"convert","outputs":[{"name":"convertedAmount","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"}];
// 合约地址
var address = "0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6";
var tokenContract = new web3.eth.Contract(abi, address);
// 调用函数
tokenContract.methods.getBalance("0x627306090abab3a6e1400e9345bc60c78a8bef57").call(null,function(error,result){
        console.log("getBalance "+result);
});
tokenContract.methods.getBalanceInEth("0x627306090abab3a6e1400e9345bc60c78a8bef57").call(null,function(error,result){
        console.log("getBalanceInEth "+result);
});	
tokenContract.methods.getBalance("0xf17f52151ebef6c7334fad080c5704d77216b732").call(null,function(error,result){
        console.log("getBalance 2 "+result);
});	
tokenContract.methods.sendCoin("0xf17f52151ebef6c7334fad080c5704d77216b732",387).send({from: '0x627306090abab3a6e1400e9345bc60c78a8bef57'})
.on('transactionHash', function(hash){
}).on('confirmation', function(confirmationNumber, receipt){
}).on('receipt', function(receipt){
    // receipt example
    console.log(receipt); //查询这里可以得到结果
}).on('error', console.error); // If a out of gas error, the second parameter is the receipt.
			
			

运行结果

			
getBalance 10000
getBalanceInEth 20000
getBalance 2 0
{ transactionHash: '0x0f7514413865219d70873634c00d1b4746c5faa436283786f5414b483b6d6333',
  transactionIndex: 0,
  blockHash: '0x5a7662d14f78b6b4d64b05c0ec1e1e641bd39440467f0476409f4e49c21f287a',
  blockNumber: 11,
  gasUsed: 51024,
  cumulativeGasUsed: 51024,
  contractAddress: null,
  status: 1,
  events: 
   { Transfer: 
      { logIndex: 0,
        transactionIndex: 0,
        transactionHash: '0x0f7514413865219d70873634c00d1b4746c5faa436283786f5414b483b6d6333',
        blockHash: '0x5a7662d14f78b6b4d64b05c0ec1e1e641bd39440467f0476409f4e49c21f287a',
        blockNumber: 11,
        address: '0xFB88dE099e13c3ED21F80a7a1E49f8CAEcF10df6',
        type: 'mined',
        id: 'log_8b567824',
        returnValues: [Result],
        event: 'Transfer',
        signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
        raw: [Object] } } }
			
			

再次运行

			
getBalance 9613
getBalanceInEth 19226
getBalance 2 387
{ transactionHash: '0x6d33487d9067494288190f7a64b3118c20560479e01afe8d53e37ca46a6001e4',
  transactionIndex: 0,
  blockHash: '0x28f6d416c68dbbd698d1a0b0fdc4ce842694d82318a38ea6395413fd15397642',
  blockNumber: 12,
  gasUsed: 36024,
  cumulativeGasUsed: 36024,
  contractAddress: null,
  status: 1,
  events: 
   { Transfer: 
      { logIndex: 0,
        transactionIndex: 0,
        transactionHash: '0x6d33487d9067494288190f7a64b3118c20560479e01afe8d53e37ca46a6001e4',
        blockHash: '0x28f6d416c68dbbd698d1a0b0fdc4ce842694d82318a38ea6395413fd15397642',
        blockNumber: 12,
        address: '0xFB88dE099e13c3ED21F80a7a1E49f8CAEcF10df6',
        type: 'mined',
        id: 'log_e5cfee46',
        returnValues: [Result],
        event: 'Transfer',
        signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
        raw: [Object] } } }

			
			

第二次运行会打印出账号2的余额

代码优化,由于开发阶段 abi 接口经常变化,从 solc 编译的abi 文件中直接读取最为方便。

			
const fs = require ('fs');
var Web3 = require("web3");
var web3 = new Web3("http://localhost:9545");
var abi = JSON.parse(fs.readFileSync('MetaCoin.abi'));
var address = "0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6";
var tokenContract = new web3.eth.Contract(abi, address);
tokenContract.methods.getBalance("0x627306090abab3a6e1400e9345bc60c78a8bef57").call(null,function(error,result){
        console.log("getBalance "+result);
});	
			
			

16.7.4. event

下面以ERC20代币为例演示事件如何工作

			
neo@MacBook-Pro ~/ethereum/web3 % cat event.js 

fs = require('fs');
var net = require('net');
var Web3 = require('web3');
var web3 = new Web3('/Users/neo/Library/Ethereum/geth.ipc', net);

console.log(web3.version)
const abi = fs.readFileSync('output/TokenERC20.abi', 'utf-8');

const contractAddress = "0x70682386d0dE84B1e549DC3c4305CCB2D261b2a8";
const coinbase = "0xB94054c174995AE2A9E7fcf6c7924635FBa8ECF7";
const toAddress = "0xf56b81a2bcb964D2806071e9Be4289A5559BB0fA";

balanceWei = web3.eth.getBalance(coinbase);
console.log(balanceWei);

const contract = new web3.eth.Contract(JSON.parse(abi), contractAddress, { from: coinbase , gas: 100000});

contract.events.Transfer({
    fromBlock: 0,
    toBlock:'latest'
}, function(error, event){ console.log(event); })
.on('data', function(event){
    console.log(event); // same results as the optional callback above
})
.on('changed', function(event){
    // remove event from local database
})
.on('error', console.error);
			
			

运行后程序不会退出,会源源不断的打印出每笔交易

			
neo@MacBook-Pro ~/ethereum/web3 % node event.js 

{ address: '0x70682386d0dE84B1e549DC3c4305CCB2D261b2a8',
  blockNumber: 0,
  transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
  transactionIndex: 0,
  blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
  logIndex: 0,
  removed: false,
  id: 'log_247b1779',
  returnValues: 
   Result {
     '0': '0xB94054c174995AE2A9E7fcf6c7924635FBa8ECF7',
     '1': '0xf56b81a2bcb964D2806071e9Be4289A5559BB0fA',
     '2': '10000000',
     from: '0xB94054c174995AE2A9E7fcf6c7924635FBa8ECF7',
     to: '0xf56b81a2bcb964D2806071e9Be4289A5559BB0fA',
     value: '10000000' },
  event: 'Transfer',
  signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
  raw: 
   { data: '0x0000000000000000000000000000000000000000000000000000000000989680',
     topics: 
      [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
        '0x000000000000000000000000b94054c174995ae2a9e7fcf6c7924635fba8ecf7',
        '0x000000000000000000000000f56b81a2bcb964d2806071e9be4289a5559bb0fa' ] } }