Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 481
Martin Avatar asked Jul 14 '19 12:07

Martin


People also ask

Are events stored on the blockchain?

Events are not stored in the transaction nor on the blockchain. They are stored within the transaction receipt.

How smart contract communicates about event to front end?

Smart contract events are a way for your contract to communicate that something happened (i.e. there was an event) on the blockchain to your front-end application, which can be 'listening' for specific events and take action when they happen.

How are smart contracts triggered?

A smart contract works through automated conditional performance. When a contractual obligation is met, the corresponding obligation is triggered. For example, an obligation could be triggered by: a specific event (“if X happens, then action Y”)


1 Answers

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

like image 127
ferit Avatar answered Oct 19 '22 09:10

ferit