通过web3.js与以太坊智能合约交互

  web3.js是以太坊提供的一个NodeJs库,封装了一些和以太坊交互的常用对象、函数。主要包括版本、账号、交易、合约等信息的获取和调用。本文主要介绍通过web3.js调用及执行智能合约

准备工作:
安装node,npm,这里就不介绍了
本文中使用的私有链是Ganache,它是可视化私有链,前身就是大名鼎鼎的testRPC。更重要的是只有在产生交易时它才会挖矿,很适合用来测试智能合约。

1、新建项目
由于web3.js 1.x 和 0.x版本api不同,尽管web3.js的1.0 release还没出来,这里仍然选用1.0 beta版本演示,方便后续学习

1
2
3
4
5
> mkdir demo && cd demo
> npm init

> npm install web3@1.0.0-beta.36 --save
> npm install solc --save

2、创建合约
在项目下新建MetaCoin.sol,把以下内容复制到文件中。该合约内容是使用truffle初始化项目(truffle unbox metacoin)后生成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pragma solidity ^0.4.24;

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

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

constructor() 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;
emit Transfer(msg.sender, receiver, amount);
return true;
}


function getBalance(address addr) public view returns (uint) {
return balances[addr];
}
}

3、编译合约
web3.js 1.0版本移除了compile方法,所以web3.eth.compile.solidity(source)已经不能用了。如果需要编译合约有两种方式

本文使用solc直接在项目中编译合约,方便查看编译后的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const fs = require('fs');
const Web3 = require("web3");
const solc = require('solc');

const data = fs.readFileSync('./MetaCoin.sol');
// console.log(data.toString())

const web3 = new Web3();

// Ganache默认端口7545
web3.setProvider(new Web3.providers.HttpProvider("http://localhost:7545"));

// 1 是优化器参数
const output = solc.compile(data.toString(), 1);
// console.log(output)

const bytecode = output.contracts[':MetaCoin'].bytecode;
const abi = output.contracts[':MetaCoin'].interface;

4、部署合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 第一个参数:合约的abi对象
new web3.eth.Contract(JSON.parse(abi), {
// 必填,合约发起者
from: '0x75441ac9a1d2DaAA5638beae207546c8D14a7f6d',
// 合约bytecode,也可也在deploy中传入
data: bytecode,
// 即gas limit,该交易最大可使用的Gas
gas: 4712388,
gasPrice: '1000000'
}).deploy().send().then((instance) => {
// console.log(instance)

// 合约地址
// console.log(`Address: ${instance.options.address}`);

//执行合约,只是查询状态,不需要挖矿,所以调用call方法
instance.methods.getBalance('0x75441ac9a1d2DaAA5638beae207546c8D14a7f6d').call({
//非必填,该合约方法的调用者
from: '0x6cc022FAE89414146b2A2646ca5143e23dA5b7e7'
}, function (error, result) {
console.log('error:' + error)
console.log('result:' + result)
})
})

其中要注意的是:web3.js 1.0版本和0.x版本获取账号的方法不一样,如下
web3.eth.accounts[0] web3.js 0.x版本中的方法
web3.eth.getAccounts().then(function (value) { console.log(value[0]) }) web3.js 1.0版本中的方法

5、调用已经部署过的合约,执行合约方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 相对于部署合约,多了第二个参数,即合约地址
const metaCoinContract = new web3.eth.Contract(JSON.parse(abi), '0x83A87C3DC9CD2146e29D8aCFb7470Bc86d8bFf28', {
// 非必填,合约的bytecode
data: bytecode,
// 非必填,合约的创建者
from: '0x906210387ADC119767900692fA5E59417C809642',
//Gas limit
gas: 4712388,
gasPrice: '1000000'
});


// 调用合约中的sendCoin方法
metaCoinContract.methods.sendCoin('0x84d939AD8034c3ce2A7D4D100b939d4523b9E46E', 100).send({
//非必填,该合约方法的调用者
from: '0x75441ac9a1d2DaAA5638beae207546c8D14a7f6d'
}).on('transactionHash', function (hash) {
console.log(hash)
}).on('receipt', function (receipt) {
console.log(receipt)
}).on('confirmation', function (confirmationNumber, receipt) {
console.log(confirmationNumber)
}).on('error', console.error)


// 调用合约中的getBalance方法
metaCoinContract.methods.getBalance('0x75441ac9a1d2DaAA5638beae207546c8D14a7f6d').call({
//非必填,该合约方法的调用者
from: '0x6cc022FAE89414146b2A2646ca5143e23dA5b7e7'
}, function (error, result) {
console.log('error:' + error)
console.log('result:' + result)
})

JouyPub wechat
欢迎订阅「K叔区块链」 - 专注于区块链技术学习