How to get all events for a transaction (not contract)?
Asked Answered
W

1

5

I want to get all events emitted by a Solidity contract using web3, however the .getPastEvents() method is for a contract.

This returns all events for contractInstance, however, my contract calls other contracts which also emit events.

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

I want to get all the events from a transaction, not from a contract.

Or as an alternative, even all events from a block, which I could then filter down using the transaction hash, to get what I want. Is there a function that returns all events in a block? I've looked but I cannot find one. Must I know every contract in the chain and get the events separately? Perhaps.

I have made a really simple example to illustrate.

The solidity code:

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();
    }
}

I am using Truffle to make it simple. Here is the migration file:

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

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

Here is the truffle javascript code that calls the contractA methodA which emits eventA, and calls contractB methodB which emits eventB:

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 );

  });

});

And here is the output:

$ 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)

As you can see I have to call for every contract independently. I wondered if perhaps there was a "transaction object" method to get both of these events in one call - as they, after all, are from the same transaction.

You can imagine a situation where events were emitted from many contracts in the same transaction.

Perhaps it just isn't possible, but I thought I would ask anyway.

Worriment answered 14/7, 2019 at 12:24 Comment(7)
Can you please clarify? Do you want all events from all contracts in a block?Johppa
Really I would like all the events emitted due to a transaction, but I'd settle for all the events in a block - because then I could filter them based on the event's transaction id - which would accomplish what I want. I have amended the question to reflect this possibility.Worriment
All events originating from a specific contract?Johppa
So I call (make transaction) to contractA method, that method emits an event, it also calls contractB method, this method also emits an event. I want to get both events, i.e. all events for my transaction... I feel this is simple, but perhaps I need to explain better.Worriment
I now give full code exampleWorriment
I am going to look into it as soon as I'm available.Johppa
There is no rush, it is more a curiosity really, as I attempt to improve my solidity skills.Worriment
J
11

Transactions that are triggered by the invocation of instanceA.methodA() are called internal transactions and their events are not included when you try to get events.

There is a way to get all events from a transaction, but it's a bit cumbersome:

1 - Get TX receipt using web3.eth.getTransactionReceipt(). This gives you logs array with event objects which has three important fields: address, data and topics.

  • address, the contract address the event fired from.
  • data, non-indexed arguments of the event.
  • topics, the hash of event signature and the hash of indexed parameters (if there are any)

2 - Having this information, use address to get contract abi. Now you have the list of all event this contract can fire because you the abi. Hash all the event signatures and find the one which matches the first item in the topics array. You can use web3.eth.abi.encodeEventSignature() to check hash of signature of an event. This way you will find which event it is. Also the parameter names.

3 - Decode abi using the event signature with web3.eth.abi.decodeLog(inputs, hexString, topics)

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

And you get:

Result { '0': 'Hello%!', '1': '62224', '2': '16', myString: 'Hello%!', myNumber: '62224', mySmallNumber: '16'


Also explained here: https://codeburst.io/deep-dive-into-ethereum-logs-a8d2047c7371

Johppa answered 22/7, 2019 at 19:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.