区块链开发之Solidity编程基础(四)合约事件

事件

事件是以太坊虚拟机(EVM)日志基础设施提供的一个便利接口。用于获取当前发生的事件。

事件和日志有三个用途:

  • 智能合约返回值给用户接口
  • 异步的带数据的触发器
  • 一种比较便宜的存储

智能合约返回值给用户接口

我们可以在 dapp 的用户界面中监听事件,EVM 的日志机制可以反过来“调用”用来监听事件的 Javascript 回调函数。

contract ExampleContract {
    
    
	//一些状态变量
	function foo(int256 value) returns (int256) {
    
    
		//改变状态
		return value;
	}
}

假设exampleContract就是ExampleContract的一个实例,一个前端使用web3.js,通过模拟函数的执行可以获得返回值:

	var returnValue = exampleContract.foo.call(2);
	console.log(returnValue);

但是一旦提交合约调用成为一个交易,则无法得到返回值

	var returnValue = exampleContract.foo.sendTransaction(2,{
    
    from: web3.eth.coinbase});
	//交易的话,这里应该是一个交易哈希值
	console.log(returnValue);

sendTransaction函数的返回值总是创建交易的哈希。交易并不返回值给前端,这是因为交易没有马上被打包,还未上链。如果想获得一个返回值,那么应该使用事件的方式。

contract ExampleContract {
    
    
	event ReturnValue(address indexed from, int256 value);
	function foo(int256 value) returns (int256) {
    
    
		//触发事件
		ReturnValue(msg.sender, value);
		return value;
	}
}

前端获取返回值:

//调用事件
var returnValue = exampleContract.ReturnValue({
    
     from:web3.eth.coinbase});
//监听事件
exampleEvent.watch(function(err, result){
    
    
	if (err) {
    
    
		console.log(err);
		return;
	}
	console.log(result.args.value);
	//其他业务
	//exampleEvent.stopWatching();
})
//调用foo上链后,watch的回调会被触发,那么前端可以从foo获得返回值
exampleContract.foo.sendTransaction(2,{
    
    from: web3.eth.coinbase});

异步的带数据的触发器

同理,前端应用可以采用监听事件进行回调的方式来实现异步操作

一种比较便宜的存储

在EVM里,事件被认为是日志(有LOG opcodes),数据可以被存储到日志,当事件被发送时,相应的日志被写到区块链中。根据这种特性,Logs被设计用来成为一种存储,它话费的燃料小于合约的storage,在EVM中Logs基本上每个字节会花费8Gas,而合约storage每32bytes话费20000Gas。
但是Logs不能被任何合约访问

扫描二维码关注公众号,回复: 14594892 查看本文章

那如何用来做存储呢,假设场景:合约可以记录某个用户当前的余额,也可以记录每个用户的充值记录,但是可以在Logs记录来取代在合约里记录,减少花费。

contract CryptoExchange {
    
    
	event Deposit(uint256 indexed market, address indexed sender, uint256 amount, uint256 time);

    function deposit(uint256 amount, uint256 market) returns (int256) {
    
    
        Deposit(market, msg.sender, amount, now);
    }
}

cryptoExContract是web3实例化的CryptoExchange,在前端调用

var depositEvent = cryptoExContract.Deposit({
    
    sender:userAddress});
depositEvent.watch(function(err, result){
    
    
	if (err) {
    
    
		console.log(err)
		return
	}
})

注意我们的事件参数sender使用关键字indexed修饰,表示索引,用于提高用户获取所有event的效率。
我们也可以从block0开始获取上链的事件

var depositEventAll = cryptoExContract.Deposit(
{
    
    sender:userAddress},
{
    
    fromBlock:0, toBlock:'latest'}
);
depositEventAll.watch(function(err, result){
    
    
	if (err) {
    
    
		console.log(err)
		return
	}
})

事件里的Indexed参数

一个事件最多三个indexed参数,在用户界面上可以使用 indexed 参数的特定值来进行过滤。
例如合约事件transfer:

event Transfer(address indexed from, address indexed to, uint256 amount);

假设应用监听事件,

1、监听from发送的事件

tokenContract.Transfer(
{
    
    from:senderAddress}
);

2、监听to接收的事件

tokenContract.Transfer(
{
    
    to:receiverAddress}
);

3、监特定from到特定to的事件

tokenContract.Transfer(
{
    
    from:senderAddress},
{
    
    to:receiverAddress}
);

猜你喜欢

转载自blog.csdn.net/BBinChina/article/details/123546764