4

I'm trying to get an event args in web3j version 4.0.1.
I use the simple syntax which is explained in the docs. I'm getting the log object but there is no args property or such thing in it. Here is my code:

EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST,
    DefaultBlockParameterName.LATEST, <contract-address>);
web3j.ethLogFlowable(filter).subscribe(log -> {
    System.out.println(log);
});

I expected to find args in the log it is not there. There is a data index and its value is some unreadable hex string. I must mention that I'm able to see the log arguments in remix and even in truffle test transaction receipt. But no success in web3j.

3 Answers 3

5

For the following event:

event MyEvent(address indexed _arg1, bytes32 indexed _arg2, uint8 _arg3); 

You can extract the event arguments from the log like this

// Event definition
public static final Event MY_EVENT = new Event("MyEvent", Arrays.<TypeReference<?>>asList(new TypeReference<Address>(true) {}, new TypeReference<Bytes32>(true) {}, new TypeReference<Uint8>(false) {}));

// Event definition hash
private static final String MY_EVENT_HASH = EventEncoder.encode(MY_EVENT);

// Filter
EthFilter filter = new EthFilter(DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST, <contract-address>);

// Pull all the events for this contract
web3j.ethLogFlowable(filter).subscribe(log -> {
    String eventHash = log.getTopics().get(0); // Index 0 is the event definition hash

    if(eventHash.equals(MY_EVENT_HASH)) { // Only MyEvent. You can also use filter.addSingleTopic(MY_EVENT_HASH) 
        // address indexed _arg1
        Address arg1 = (Address) FunctionReturnDecoder.decodeIndexedValue(log.getTopics().get(1), new TypeReference<Address>() {});
        // bytes32 indexed _arg2
        Bytes32 arg2 = (Bytes32) FunctionReturnDecoder.decodeIndexedValue(log.getTopics().get(2), new TypeReference<Bytes32>() {});
        // uint8 _arg3
        Uint8 arg3 = (Uint8) FunctionReturnDecoder.decodeIndexedValue(log.getTopics().get(3), new TypeReference<Uint8>() {});
    }

});
4
  • 3
    That worked! Thank you. The web3j docs needs clarifying and some examples. Commented Jan 1, 2019 at 12:42
  • 2
    _arg3 in your example is not indexed therefore it will be available through: Uint8 arg3 = (Uint8) FunctionReturnDecoder.decodeIndexedValue(log.getData(), new TypeReference<Uint8>() {}); Commented Feb 4, 2022 at 5:02
  • 2
    for non indexed data: List<Type> nonIndexedValues = FunctionReturnDecoder.decode(log.getData(), event.getNonIndexedParameters());
    – Evans Y.
    Commented Sep 23, 2022 at 6:29
  • Either I don't understand this answer or it didn't decode Event data with Web3j 4.8.7. But @unlimittt's answer below did work for me.
    – devdanke
    Commented Mar 8, 2023 at 10:28
2

Generate contracts with web3j-cli:

web3j generate truffle --truffle ./artifacts/contracts/MyContract.sol/MyContract.json --outputDir web3j --package com.example.contracts

Check out event methods on generated contract:

get[EventName]Events(TransactionReceipt transactionReceipt) {}

These methods automatically create Class with parsed topic values.

The key-method there is extractEventParametersWithLog(Event event, Log log) which exists on every Contract

0
2

So I've been attempting to write a unit test for testing an Event in Kotlin, this might help someone (or help contribute to unit testing events here).

build.gradle:

plugins {
    id 'java'
    id 'org.jetbrains.kotlin.jvm' version '1.4.10'
    id 'application'
    id "com.github.johnrengelman.shadow" version "5.2.0"
    id 'org.web3j' version '4.8.4'
}
...
dependencies {
    implementation "org.web3j:core:$web3jVersion",
            "ch.qos.logback:logback-core:$logbackVersion",
            "ch.qos.logback:logback-classic:$logbackVersion"
    implementation "org.web3j:web3j-unit:$web3jVersion"
    implementation "org.web3j:web3j-evm:$web3jVersion"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
}
...

Say I have a contract with the following:

MyContract.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MyContract {
    uint public someNumber;

    event NumberIncremented(uint someNumber);

    constructor() public {
        someNumber = 0;
    }

    function incrementNumber() public {
        someNumber = someNumber + 1;
        emit NumberIncremented(someNumber);
    }
}

MyContractTests.kt looks like the following:

@EVMTest(NodeType.EMBEDDED)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyContractTests {
  private lateinit var myContract: MyContract

  @BeforeAll
  fun deploy(
    web3j: Web3j,
    transactionManager: TransactionManager,
    contractGasProvider: ContractGasProvider
  ) {
    myContract = MyContract.deploy(web3j, transactionManager, contractGasProvider).send()
  }

  @Test
  fun `increment number and emit event`() {
    val transactionReceiptVal = myContract.incrementNumber().send()

    val response = myContract.getNumberIncrementedEvents(transactionReceiptVal)
    Assertions.assertEquals(1, response.size)
  }
}

I've tried Greg Jeanmart's method in the unit test, however it seems like using the EmbeddedWeb3JService throws an UnsupportedOperationException for eth_filter when using send().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.