以太坊系列之十七: 使用web3进行合约部署调用以及监听
以太坊系列之十七: 使用web3进行智能合约的部署调用以及监听事件(Event)
上一篇介绍了使用golang进行智能合约的部署以及调用,但是使用go语言最大的一个问题是没法持续监听事件的发生.
比如我的后台程序需要监控谁给我转账了,如果使用go语言,目前就只能是轮询数据,而使用web3就简单许多,geth会把我关心的事件
主动通知给我.
token合约
token合约是官方提供的一个样例,这里给出我修改过的版本,方便演示.
contract MyToken {
/* Public variables of the token */
string public name;
string public symbol;
uint8 public decimals;
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint)) public allowance;
mapping (address => mapping (address => uint)) public spentAllowance;
/* This generates a public event on the blockchain that will notify clients */
event Transfer(address indexed from, address indexed to, uint256 value);
event ReceiveApproval(address _from, uint256 _value, address _token, bytes _extraData);
/* Initializes contract with initial supply tokens to the creator of the contract */
function MyToken(uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) {
balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = tokenSymbol; // Set the symbol for display purposes
decimals = decimalUnits; // Amount of decimals for display purposes
}
/* Send coins */
function transfer(address _to, uint256 _value) {
if (balanceOf[msg.sender] < _value) throw; // Check if the sender has enough
if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
balanceOf[msg.sender] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place
}
/* Allow another contract to spend some tokens in your behalf */
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
allowance[msg.sender][_spender] = _value;
ReceiveApproval(msg.sender, _value, this, _extraData);
}
/* A contract attempts to get the coins */
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
if (balanceOf[_from] < _value) throw; // Check if the sender has enough
if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows
if (spentAllowance[_from][msg.sender] + _value > allowance[_from][msg.sender]) throw; // Check allowance
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
spentAllowance[_from][msg.sender] += _value;
Transfer(msg.sender, _to, _value);
}
/* This unnamed function is called whenever someone tries to send ether to it */
function () {
throw; // Prevents accidental sending of ether
}
}
编译token
通过solc编译得到的json abi以及hex编码的code,由于太长,太占地方就不放上来了.
这些会在部署以及调用的时候用到.
下面来通过web3进行合约的部署,读取数据,发起事务以及监听事件的发生.
部署合约
部署合约需要创建事务,话费gas,因此相关账户必须事先解锁,web3目前没有api可以解锁账户,需要自己在控制台事先解锁才行.
首先创建合约,需要制定abi.
然后就可以部署合约了,这时候需要合约的code,
部署合约的结果可以通过异步函数来获取,例子已经给出.
var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.IpcProvider("\\\\.\\pipe\\geth.ipc",net));
var eth=web3.eth;
var tokenContract = new web3.eth.Contract(MyTokenABI, null, {
from: '0x1a9ec3b0b807464e6d3398a59d6b0a369bf422fa' // 目前web3没有api来解锁账户,只能自己事先解锁
});
tokenContract.deploy({
data: MyTokenBin,
arguments: [32222, 'token on web3',0,'web3']
}).send({
from: '0x1a9ec3b0b807464e6d3398a59d6b0a369bf422fa',
gas: 1500000,
gasPrice: '30000000000000'
}, function(error, transactionHash){
console.log("deploy tx hash:"+transactionHash)
})
.on('error', function(error){ console.error(error) })
.on('transactionHash', function(transactionHash){ console.log("hash:",transactionHash)})
.on('receipt', function(receipt){
console.log(receipt.contractAddress) // contains the new contract address
})
.on('confirmation', function(confirmationNumber, receipt){console.log("receipt,",receipt)})
.then(function(newContractInstance){
console.log(newContractInstance.options.address) // instance with the new contract address
});
查询合约
合约部署成功以后,有了地址就可以根据地址来查询合约的状态.
查询合约状态并不需要发起事务,也不需要花费gas,因此比较简单.
var tokenContract = new web3.eth.Contract(MyTokenABI, '0x6a0dF9E94a41fCd89d8236a8C03f9D678df5Acf9');
tokenContract.methods.name().call(null,function(error,result){
console.log("contract name "+result);
})
调用合约函数
合约的函数除了指明返回值是constant的以外,都需要发起事务,这时候就需要指定调用者,因为要花费该账户的gas.
这里调用一下transfer函数.
tokenContract.methods.transfer("0x8c1b2e9e838e2bf510ec7ff49cc607b718ce8401",387).send({from: '0x1a9ec3b0b807464e6d3398a59d6b0a369bf422fa'})
.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.
监听事件
刚刚调用transfer的时候还会触发合约的事件Transfer,如果程序关注谁给谁进行了转账,那么就可以通过监听该事件.
通过指定fromBlock,toBlock可以限制事件发生的范围,除了这个还有一个filter参数可以进行更详细的限制,
如有兴趣可以查询文档web3文档
tokenContract.events.Transfer({
fromBlock: 0,
toBlock:'latest'
}, function(error, event){ /*console.log("result:\n"+JSON.stringify(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);
需要说明的是,这个监听不仅传送历史事件,未来所有事件也会传送.
下面的结果是首先运行程序,会先打印出来刚刚进行的转账.
然后我在通过其他途径进行转账操作,这时候终端会把新的转账也打印出来.
历史转账:
{ address: '0x6a0dF9E94a41fCd89d8236a8C03f9D678df5Acf9',
blockNumber: 10680,
transactionHash: '0x27d5ab9277df504a436b1068697a444d30228584094632f10ab7ba5213a4eccc',
transactionIndex: 0,
blockHash: '0xcde734882b0d8cb7a5bf1f7e6d1ccfac5365308de2d7391ce286b45c5546f40b',
logIndex: 0,
removed: false,
id: 'log_2588a961',
returnValues:
Result {
'0': '0x1a9eC3b0b807464e6D3398a59d6b0a369Bf422fA',
'1': '0x8c1b2E9e838e2Bf510eC7Ff49CC607b718Ce8401',
'2': '387',
from: '0x1a9eC3b0b807464e6D3398a59d6b0a369Bf422fA',
to: '0x8c1b2E9e838e2Bf510eC7Ff49CC607b718Ce8401',
value: '387' },
event: 'Transfer',
signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
raw:
{ data: '0x0000000000000000000000000000000000000000000000000000000000000183',
topics:
[ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x0000000000000000000000001a9ec3b0b807464e6d3398a59d6b0a369bf422fa',
'0x0000000000000000000000008c1b2e9e838e2bf510ec7ff49cc607b718ce8401' ] } }
实时监控到的转账
{ address: '0x6a0dF9E94a41fCd89d8236a8C03f9D678df5Acf9',
blockNumber: 10740,
transactionHash: '0xb42651720e8b8b64283cbd245aebaa7ad7e3dda58b9887f645ad6957bd7771b8',
transactionIndex: 0,
blockHash: '0xcdca97ba5a277e402a93188df03a758c916c37eea0f7498365d227ebd7cb2ee2',
logIndex: 0,
removed: false,
id: 'log_edc5dc68',
returnValues:
Result {
'0': '0x1a9eC3b0b807464e6D3398a59d6b0a369Bf422fA',
'1': '0x0000000000000000000000000000000000000001',
'2': '32',
from: '0x1a9eC3b0b807464e6D3398a59d6b0a369Bf422fA',
to: '0x0000000000000000000000000000000000000001',
value: '32' },
event: 'Transfer',
signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
raw:
{ data: '0x0000000000000000000000000000000000000000000000000000000000000020',
topics:
[ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x0000000000000000000000001a9ec3b0b807464e6d3398a59d6b0a369bf422fa',
'0x0000000000000000000000000000000000000000000000000000000000000001' ] } }
以太坊系列之十七: 使用web3进行合约部署调用以及监听的更多相关文章
- 以太坊系列之十六: 使用golang与智能合约进行交互
以太坊系列之十六: 使用golang与智能合约进行交互 以太坊系列之十六: 使用golang与智能合约进行交互 此例子的目录结构 token contract 智能合约的golang wrapper ...
- 以太坊系列之十六:golang进行智能合约开发
以太坊系列之十六: 使用golang与智能合约进行交互 以太坊系列之十六: 使用golang与智能合约进行交互 此例子的目录结构 token contract 智能合约的golang wrapper ...
- 以太坊系列之一: 以太坊RLP用法-以太坊源码学习
RLP (递归长度前缀)提供了一种适用于任意二进制数据数组的编码,RLP已经成为以太坊中对对象进行序列化的主要编码方式.RLP的唯一目标就是解决结构体的编码问题:对原子数据类型(比如,字符串,整数型, ...
- 以太坊系列之十八: 百行go代码构建p2p聊天室
百行go代码构建p2p聊天室 百行go代码构建p2p聊天室 1. 上手使用 2. whisper 原理 3. 源码解读 3.1 参数说明 3.1 连接主节点 3.2 我的标识 3.2 配置我的节点 3 ...
- 以太坊系列之十一: 零起步使用remix开发智能合约
一步一步使用remix开发智能合约 最新版的remix(2017-8-3)只能使用在线开发了,已经没有离线版本了,并且好像在线版本要FQ才能访问(自行解决). 1.打开remix 注意地址如果是htt ...
- 以太坊系列之六: p2p模块--以太坊源码学习
p2p模块 p2p模块对外暴露了Server关键结构,帮助上层管理复杂的p2p网路,使其集中于Protocol的实现,只关注于数据的传输. Server使用discover模块,在指定的UDP端口管理 ...
- 以太坊系列之四: 使用atomic来避免lock
使用atomic来避免lock 在程序中为了互斥,难免要用锁,有些时候可以通过使用atomic来避免锁, 从而更高效. 下面给出一个以太坊中的例子,就是MsgPipeRW,从名字Pipe可以看出, 他 ...
- 以太坊系列之三: 以太坊的crypto模块--以太坊源码学习
以太坊的crypto模块 该模块分为两个部分一个是实现sha3,一个是实现secp256k1(这也是比特币中使用的签名算法). 需要说明的是secp256k1有两种实现方式,一种是依赖libsecp2 ...
- 以太坊系列之二: 单调时间monotime-以太坊源码学习
在程序中需要测量时间时最好使用monotime.Now()而不是time.Now(),相比之下前者更准确. 来个示例: func main() { var start, elapsed time.Du ...
随机推荐
- TELNET协议规范
ARPA Internet上的主机被要求采用并实现此标准. 介绍 TELNET Protocol的目的是提供一个相对通用的,双向的,面向八位字节的通信方法.它主要的目标是允许接口终端设备的标准方法和面 ...
- 1108 Finding Average
题意:根据条件判定哪些数是合法的,哪些是不合法的.求其中合法的数的平均值. 思路:字符串处理函数,考虑到最后输出的时候需要控制格式,因此选用scanf()和printf().另外需要了解atof()函 ...
- 1140 Look-and-say Sequence
题意:略 思路:第1个数是1(读作“1有1个”),因此第2个数就是11(读作“1有2个”),因此第3个数就是12(读作“1有1个,2有1个”),因此第4个数是1121(读作“1有2个,2有1个,1有1 ...
- 什么是DA控制
液压系统中的DA控制: Automotive Drive control & Anti stall control 自动变速控制 & 防熄火控制 (DA控制) 1:自动(变速)驱动控制 ...
- Python中特殊函数和表达式 filter,map,reduce,lambda
1. filter 官方解释:filter(function or None, sequence) -> list, tuple, or string Return those items of ...
- Oracle11gR2--SEC_CASE_SENSITIVE_LOGON参数解析
在Oracle的11g之前的版本中密码是不区分大小写的(使用双引号强制除外). 在Oracle的11g以及以后的版本中对此有所增强.从此密码有了大小写的区分. 这个大小写敏感特性是通过SEC_CASE ...
- mixer音量的设置:amixer小工具的…
1.关于alsa-utils和lib的移植我的上一篇博文中已经说明了,下面我就来说说我的混音器mixer音量控制的调节过程,网上的很多方法都是比较基本的入门没有知名具体的操作方法,在此我来谈谈我的设置 ...
- Log4j配置很详细
来自: http://www.blogjava.net/zJun/archive/2006/06/28/55511.html Log4J的配置文件(Configuration File)就是用来设置记 ...
- codeforces:Helga Hufflepuff's Cup
题目大意:有一个包含n个顶点的无向无环连通图G,图中每个顶点都允许有一个值type,type的范围是1~m.有一个特殊值k,若一个顶点被赋值为k,则所有与之相邻的顶点只能被赋小于k的值.最多有x个顶点 ...
- Gym 101350G - Snake Rana
题意 有一个n*m的矩形,里面有k个炸弹,给出每个炸弹的坐标,计算在n*m的矩形中有多少子矩形内是不包含炸弹的. 分析 场上很是懵逼,赛后问学长说是容斥定理?一脸懵逼..容斥不是初中奥数用在集合上的东 ...