如何获取交易的所有事件(不是合约)?

Posted

技术标签:

【中文标题】如何获取交易的所有事件(不是合约)?【英文标题】:How to get all events for a transaction (not contract)? 【发布时间】:2019-11-23 10:14:36 【问题描述】:

我想使用 web3 获取 Solidity 合约发出的所有事件,但是 .getPastEvents() 方法适用于合约。

这会返回 contractInstance 的所有事件,但是,我的合约会调用其他也发出事件的合约。

await contractInstance.getPastEvents("allEvents", fromBlock: bn, toBlock: bn);

我想从交易中获取所有事件,而不是从合约中获取。

或者作为替代方案,甚至是来自一个块的所有事件,然后我可以使用事务哈希对其进行过滤,以获得我想要的。是否有一个函数可以返回一个块中的所有事件?我看过,但我找不到。我必须知道链中的每个合约并分别获取事件吗?也许吧。

我做了一个非常简单的例子来说明。

solidity 代码:

pragma solidity 0.5.8;

contract contractA 
    event eventA();
    function methodA( address b ) public 
        emit eventA();
        contractB instanceB = contractB( b );
        instanceB.methodB();
    


contract contractB 
    event eventB();
    function methodB() public 
        emit eventB();
    

我正在使用 Truffle 来简化它。这是迁移文件:

var contractA = artifacts.require("contractA");
var contractB = artifacts.require("contractB");

module.exports = function(deployer) 
  deployer.deploy(contractA);
  deployer.deploy(contractB);

这里是 truffle javascript 代码,它调用发送 eventA 的contractA methodA,并调用发送eventB 的contractB methodB:

const contractA = artifacts.require("contractA");
const contractB = artifacts.require("contractB");

contract("contractA", async accounts => 

  thisAccount = accounts[0];

  it( "Simple test", async () => 

    const instanceA = await contractA.deployed();
    const instanceB = await contractB.deployed();

    const transaction = await instanceA.methodA( instanceB.address,  from: thisAccount  );

    const bn = transaction.receipt.blockNumber, txHash = transaction.tx;

    const allEventsA = await instanceA.getPastEvents("allEvents", fromBlock: bn, toBlock: bn);
    const allEventsB = await instanceB.getPastEvents("allEvents", fromBlock: bn, toBlock: bn);

    console.log("A");
    console.log( allEventsA );

    console.log("B");
    console.log( allEventsB );

  );

);

这是输出:

$ truffle test test.js
Using network 'development'.


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
  Contract: contractA
A
[
  
    logIndex: 0,
    transactionIndex: 0,
    transactionHash: '0xe99db12863e5c0a0ae2c9c603d9d29f46a74d45ee9bf9f56d15f6f7bd1888058',
    blockHash: '0xfa65496b8cb6ecf5b729892836adf80aa883e6823bbdb2d1b8cdfe61b5c97256',
    blockNumber: 1573,
    address: '0x97519Ada953F882d61625125D5D68E7932250E9F',
    type: 'mined',
    id: 'log_d28138a2',
    returnValues: Result ,
    event: 'eventA',
    signature: '0x72f2637d8047e961ba6b558fdf63d428e9734bdf7ee2fb2b114f3b1aa65335c7',
    raw:  data: '0x', topics: [Array] ,
    args: Result  __length__: 0 
  
]
B
[
  
    logIndex: 1,
    transactionIndex: 0,
    transactionHash: '0xe99db12863e5c0a0ae2c9c603d9d29f46a74d45ee9bf9f56d15f6f7bd1888058',
    blockHash: '0xfa65496b8cb6ecf5b729892836adf80aa883e6823bbdb2d1b8cdfe61b5c97256',
    blockNumber: 1573,
    address: '0x00108B6A5572d95Da87e8b4bbF1A3DcA2a565ff7',
    type: 'mined',
    id: 'log_da38637d',
    returnValues: Result ,
    event: 'eventB',
    signature: '0x34a286cd617cdbf745989ac7e8dab3f95e8bb2501bcc48d9b6534b73d055a89c',
    raw:  data: '0x', topics: [Array] ,
    args: Result  __length__: 0 
  
]
    ✓ Simple test (76ms)

如您所见,我必须独立调用每份合同。我想知道是否有一种“事务对象”方法可以在一次调用中获取这两个事件 - 因为它们毕竟来自同一个事务。

您可以想象在同一交易中从多个合约发出事件的情况。

也许这是不可能的,但我想我还是会问。

【问题讨论】:

你能澄清一下吗?你想要一个区块中所有合约的所有事件吗? 真的我想要所有因交易而发出的事件,但我会解决一个块中的所有事件 - 因为我可以根据事件的交易 ID 过滤它们 - 这将完成我想要的是。我已经修改了问题以反映这种可能性。 所有源自特定合约的事件? 所以我调用(make transaction)到contractA方法,那个方法发出一个事件,它也调用contractB方法,这个方法也发出一个事件。我想获得这两个事件,即我的交易的所有事件......我觉得这很简单,但也许我需要更好地解释。 我现在给出完整的代码示例 【参考方案1】:

调用instanceA.methodA() 触发的事务称为内部事务,当您尝试获取事件时,它们的事件不包括在内。

有一种方法可以从一个事务中获取所有事件,但是有点麻烦:

1 - 使用web3.eth.getTransactionReceipt() 获取 TX 收据。这为您提供了包含三个重要字段的事件对象的日志数组:addressdatatopics

address,触发事件的合约地址。 data,事件的非索引参数。 topics,事件签名的哈希和索引参数的哈希(如果有的话)

2 - 有了这些信息,使用address 获取合同 abi。现在你有了这个合约可以触发的所有事件的列表,因为你是 abi。散列所有事件签名并找到与topics 数组中的第一项匹配的事件签名。您可以使用web3.eth.abi.encodeEventSignature() 检查事件签名的哈希值。这样,您将找到它是哪个事件。还有参数名称。

3 - 使用 web3.eth.abi.decodeLog(inputs, hexString, topics) 的事件签名解码 abi

例如:web3.eth.abi.decodeLog([ type: 'string', name: 'myString' , type: 'uint256', name: 'myNumber', indexed: true , type: 'uint8', name: 'mySmallNumber', indexed: true ], '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000748656c6c6f252100000000000000000000000000000000000000000000000000', ['0x000000000000000000000000000000000000000000000000000000000000f310', '0x0000000000000000000000000000000000000000000000000000000000000010']);

你会得到:

结果 '0': '你好%!', '1': '62224', '2': '16', myString: '你好%!', 我的号码:'62224', mySmallNumber: '16'


这里也有解释:https://codeburst.io/deep-dive-into-ethereum-logs-a8d2047c7371

【讨论】:

以上是关于如何获取交易的所有事件(不是合约)?的主要内容,如果未能解决你的问题,请参考以下文章

以太坊交易信息及eventinputlogstopics等概念机制

智能合约如何可信的与外部世界交互

合约地址交易列表 - Etherscan API

Fabric 账本数据块结构解析:如何解析账本中的智能合约交易数据

如何通过 Python 和 Web3.py 获取 ETH 智能合约的数量?

Fabric 账本数据块结构解析:如何解析账本中的智能合约交易数据