Truffle-Contract中间件

— Truffle-Contract安装&使用 —
— 连接 MetaMask钱包 —

truffle-contact

truffle-contact 是一个基于web3的链接网络中智能合约的JS中间组件,利用它可以方便调用合约
官方英文文档

安装/引入

安装:
npm install truffle-contract
npm install web3
引入:

1
2
<script type="text/javascript" src="./path/to/web3.min.js"></script>
<script type="text/javascript" src="./dist/truffle-contract.min.js"></script>

链接至合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1.新建一个web3驱动
var provider = new Web3.providers.HttpProvider("http://localhost:8545");
//2.引入truffle-contract包
var contract = require("truffle-contract");
//3.1.输入是一个被定义为truffle0contract-schema的JSON blob
var MyContract = contract({
abi: ...,
unlinked_binary: ...,
address: ..., // optional
// many more
})
//3.2.当引入是js包时,默认名称为TruffleContract
var MyContract = TruffleContract(...);
//4.配置驱动链接至链
MyContract.setProvider(provider);

你能使用MyContract中的以下函数来连接至链:

  • at():创建一个MyContract实例在特定的位置
  • deployed():创建一个MyContract实例在合约默认的位置
  • new():部署一个此智能合约的新版本到网络,获取这个新的合约实例

合约抽象类

链接至链上合约,可以查看合约返给我们的合约抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 注意用.then 使用回调函数
MetaCoin.deployed().then(function(instance) {
console.log(instance);
});

// 输出
//
// Contract
// - address: "0xa9f441a487754e6b27ba044a5a8eb2eec77f6b92"
// - allEvents: ()
// - getBalance: ()
// - getBalanceInEth: ()
// - sendCoin: ()
// ...

这里会有部署合约的地址,合约的函数等等内容,可以由此来调用合约中的函数

truffle-contract API

在说明如何调用合约之前,以MyContract为例,详细说明truffle-contract自带的其他函数

MyContract.new([arg1, arg2, …], [tx params])

作用:将合约的一个新实例部署到网络中
输入参数:这个函数接受contract需要的任何参数。最后有一个可选参数,可传递交易参数,包括发起地址,gas限制,gas价格等。
返回:返回包含一个在新的地址部署的合约 的合约抽象类 的Promise

MyContract.at(address)

作用:从链上特定的地址上创建一个新的合约实例
输入参数:一个新的地址
返回:返回一个新的合约实例

MyContract.deployed()

作用:从默认地址创建一个新的合约实例。该默认地址是提供给truffle-contract的参数,对应对应的链
输入参数:一个新的地址
返回:返回一个新的合约实例

作用:连接一个合约虚拟实例的库到MyContract。库必须已经被部署,并拥有部署地址。并且可以从该合约虚拟实例中推断出名称跟部署地址
输入参数:一个已经部署的合约虚拟实例
返回:在这个交易的结果中,报告所有合约虚拟实例库的事件?

MyContract.link(name, address)

MyContract.link(object)

此处不太明白link的作用,差不多是拷贝吧,当用到时,再补充说明

MyContract.networks()

作用:查看MyContract的已经设置为代表的网络id列表
返回:网络id列表

MyContract.setProvider(provider)

作用:设置MyContract将要使用的web3驱动
输入参数:web3的provider

MyContract.setNetwork(network_id)

作用:设置MyContract当前代表网络id
输入参数:网络id

MyContract.hasNetwork(network_id)

作用:判断MyContract是否连接上这个网络
输入参数:网络id
返回:返回布尔型;是/否

MyContract.defaults([new_defaults])

作用:为从MyContract创建的所有实例设置事务默认值
输入参数:设置的默认值
返回:没有输入参数时,返回当前事务默认值

1
2
3
4
5
6
7
//设置新的默认值
MyContract.defaults({
from: ...,
gas: ...,
gasPrice: ...,
value: ...
})

此处设置可以省略在调用智能合约时的每次指定from,gas等

MyContract.clone(network_id)

作用:从当前MyContract克隆一个合约虚拟实例到新的网络id(别忘了,之后指定新的provider)
输入参数:network_id
返回:一个新的合约虚拟实例

1
var MyOtherContract = MyContract.clone(1337);

与合约中函数的交互

其中与合约函数交互有两种形式:transactioncall
以下为实例合约

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
34
35
pragma solidity ^0.4.18;

import "./ConvertLib.sol";

// This is just a simple example of a coin-like contract.
// It is not standards compatible and cannot be expected to talk to other
// coin/token contracts. If you want to create a standards-compliant
// token, see: https://github.com/ConsenSys/Tokens. Cheers!

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 getBalanceInEth(address addr) public view returns(uint){
return ConvertLib.convert(getBalance(addr),2);
}
//获取代币函数
function getBalance(address addr) public view returns(uint) {
return balances[addr];
}
}

transaction 交易

真实的产生了一个交易,由矿工处理

  • 消耗Gas
  • 改变网络状态
  • 不能立即处理
  • 不会有返回值(只会有交易id)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var account_one = "0x1234..."; // an address
var account_two = "0xabcd..."; // another address

var meta;

MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// 如果这个回调函数被执行, 交易被成功执行了
alert("Transaction successful!")
}).catch(function(e) {
// 有错误
})

可以注意到实际我们传入了三个参数,但是在合约的定义中是没有第三个参数的。函数允许指定事务的细节,这里指定了from地址,确保交易是account_one发起的
 
如果在事务中执行的函数有一个返回值,那么您将不会在这个结果中获得返回值。您必须使用一个事件(event)并在日志数组中查找结果。之后详细说明

call

不会真实产生交易

  • 免费(不消耗Gas)
  • 不改变网络状态
  • 立即处理
  • 会有返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
var account_one = "0x1234..."; // an address

var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one, {from: account_one});
}).then(function(balance) {
// 如果这个回调函数被执行, call被成功执行了
// 注意:这个回调函数会立即返回,不会有等待
console.log(balance.toNumber());
}).catch(function(e) {
// 有错误
})

注意以太坊网络可以处理非常大的数字,这里返回是一个BigNumber类型,我们转变为数字(我们是已知这里的数字比较小,如果超出最大整型范围,转变会出错)

捕捉事件

在合约中使用event触发事件,在 transaction 交易形式的返回内容中,会有事件结果(这也是让交易形式返回结果的方式)

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
var account_one = "0x1234..."; // an address
var account_two = "0xabcd..."; // another address

var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// result 是一个对象 有以下参数
//
// result.tx => 交易的hash, string型
// result.logs => 在交易中触发的事件的数组
// result.receipt => 交易收据对象, 包括 gas used

// 遍历 result.logs
for (var i = 0; i < result.logs.length; i++) {
var log = result.logs[i];

if (log.event == "Transfer") {
// 得到要的event
break;
}
}
}).catch(function(err) {
// 有错误
});

truffle-contract 链接Metamask web3

利用truffle-contract使MetaMask获取当前账号,连接至区块链

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//引入truffle-contract
var contract = require('truffle-contract');
//引入合约地址
var CoinContract = require('../../contracts/Coin.json');

//获取当前窗口的web3的provider
var web3 = window.web3
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider)
} else {
web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:7545/'))
}

const Contract = {
contract: null,
instance: null,
account: null,

init: function () {
let self = this

return new Promise(function (resolve, reject) {
// 引入合约地址
self.contract = contract(CoinContract)
self.contract.setProvider(web3.currentProvider)

// 获取metamak当前账户
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
self.account = accounts[0]
})
// 链接至区块链
self.contract.deployed().then(instance => {
// 返回合约
self.instance = instance
resolve()
}).catch(err => {
reject(err)
})
})

}
}

export default Contract