1

I need to use Multi Call contract to merge multiple calls,Its address on the mainnet is 0x2cc8688C5f75E365aaEEb4ea8D6a480405A48D2A

contract Multicall {
struct Call {
    address target;
    bytes callData;
}
function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) {
    blockNumber = block.number;
    returnData = new bytes[](calls.length);
    for(uint256 i = 0; i < calls.length; i++) {
        (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
        require(success);
        returnData[i] = ret;
    }
}
...
}

When I request with null value, I can get the normal return result.

     Web3j web3j = new Web3Helper("wss://mainnet.infura.io/ws/v3/xxxx").getWeb3j();
    String addressOfMulticall = "0xeefba1e63905ef1d7acba5a8513c70307c1ce441";
    //create an empty struct data
    StaticStruct staticStruct = new StaticStruct(Address.DEFAULT, new DynamicBytes(new byte[0]));
    Function function = new Function("aggregate", List.of(new DynamicArray(StaticStruct.class, staticStruct))
            , List.of(new TypeReference<StaticStruct>() {
    }));
    String encodeFunctionDataOfMulticall = FunctionEncoder.encode(function);
    //encodeFunctionDataOfMulticall-> 0x252dba420000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    //buildMethodSignature -> aggregate((address,bytes)[])
    //buildMethodId -> 0x252dba42
    //create an eth_call transaction
    Transaction ethCallTransaction = Transaction.createEthCallTransaction(Address.DEFAULT.getValue(), addressOfMulticall, encodeFunctionDataOfMulticall);
    EthCall send = web3j.ethCall(ethCallTransaction, DefaultBlockParameterName.PENDING).sendAsync().get();
    //Sending request: {"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x0000000000000000000000000000000000000000","to":"0x942EEB04AD50500a970d71a374101Ee78F87FB53","data":"0xaf36778b"},"pending"],"id":0}
    //Received message {"jsonrpc":"2.0","id":0,"result":"0x0000000000000000000000000000000000000000000000000000000000b76dd40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"}

When I pass the value is not empty, EVM execution will report an error.

Web3j web3j = new Web3Helper("wss://mainnet.infura.io/ws/v3/xxxx").getWeb3j();
    String addressOfMulticall = "0xeefba1e63905ef1d7acba5a8513c70307c1ce441";
    Address addressOfDaiTokens = new Address("0x6b175474e89094c44da98b954eedeac495271d0f");
    //solidity code: function name() returns(string memory){}
    Function nameFunction = new Function("name", List.of(), List.of(new TypeReference<DynamicBytes>() {
    }));
    String encodeDataOfNameFunction = FunctionEncoder.encode(nameFunction);
    //encodeDataOfNameFunction->0x06fdde03
    StaticStruct staticStruct = new StaticStruct(addressOfDaiTokens, new DynamicBytes(encodeDataOfNameFunction.getBytes()));
    //solidity code: function aggregate(Call[]); Call{address,bytes};
    Function aggregateFunction = new Function("aggregate", List.of(new DynamicArray(StaticStruct.class, staticStruct))
            , List.of(new TypeReference<StaticStruct>() {
    }));
    String encodeFunctionDataOfMulticall = FunctionEncoder.encode(aggregateFunction);
    //encodeFunctionDataOfMulticall-> 0x252dba42000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000a3078303666646465303300000000000000000000000000000000000000000000
    //buildMethodSignature -> aggregate((address,bytes)[])
    //buildMethodId -> 0x252dba42
    //create an eth_call transaction
    Transaction ethCallTransaction = Transaction.createEthCallTransaction(Address.DEFAULT.getValue(), addressOfMulticall, encodeFunctionDataOfMulticall);
    EthCall send = web3j.ethCall(ethCallTransaction, DefaultBlockParameterName.PENDING).sendAsync().get();
    //Sending request: {"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x0000000000000000000000000000000000000000","to":"0xeefba1e63905ef1d7acba5a8513c70307c1ce441","data":"0x252dba42000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000a3078303666646465303300000000000000000000000000000000000000000000"},"pending"],"id":0}
    //Received message {"jsonrpc":"2.0","id":0,"error":{"code":-32000,"message":"execution reverted"}}

I want to know how to encode the corresponding data in order to get a normal return. Could someone help me out a bit?

10
  • wouldn't be easier if you generated a java wrapper class of the contract and called the methods instead of decoding and create transactions by yourself?
    – Majd TL
    Mar 11 at 17:20
  • if you add comments to the code it will be easier to understand, and probably rename the variable like "address" to addressOf<what> :)
    – Majd TL
    Mar 11 at 17:22
  • I have modified it accordingly and added comments.
    – momo
    Mar 12 at 4:15
  • When I used web3j-client command tools to generate java wrapper classes, an error message appeared. I can’t solve it, so I encode the data myself. the error message is "xxxx not a invalid name".
    – momo
    Mar 12 at 5:42
  • Okay thanks will take a look at it after the work, if the contract is available then I could try to generate the Java class and see what error I get
    – Majd TL
    Mar 12 at 6:43
1

Replacing StaticStruct in the code with DynamicStruct can solve this error.At the same time, you need to pay attention to how to pass the encoded bytes parameter.And the web3j version is 4.8.4.In the 5.0.0 version of web3j, the related classes of Struct have been replaced with Tuple.

    Web3j web3j = new Web3Helper("wss://kovan.infura.io/ws/v3/xxxx").getWeb3j();
    String addressOfMulticall = "0x1aA360cdfEeae06D4ac2686fb44Dc2B804Abf2dC";
    Address addressOfDaiTokens = new Address("0xd0a1e359811322d97991e03f863a0c30c2cf029c");
    Function nameFunction = new Function("name", List.of(), List.of(new TypeReference<DynamicBytes>() {
    }));
    String encodeDataOfNameFunction = FunctionEncoder.encode(nameFunction);
    /**
     * before: StaticStruct staticStruct = new StaticStruct(addressOfDaiTokens, new DynamicBytes(encodeDataOfNameFunction.getBytes()));
     */
    DynamicStruct staticStruct = new DynamicStruct(addressOfDaiTokens, new DynamicBytes(Hex.decode(encodeDataOfNameFunction.substring(2).getBytes())));
    Function aggregateFunction = new Function("aggregate", List.of(new DynamicArray(DynamicStruct.class, staticStruct))
            , List.of(new TypeReference<Uint256>() {
    }, new TypeReference<DynamicArray<DynamicBytes>>() {
    }));
    String encodeFunctionDataOfMulticall = FunctionEncoder.encode(aggregateFunction);
    Transaction ethCallTransaction = Transaction.createEthCallTransaction(Address.DEFAULT.getValue(), addressOfMulticall, encodeFunctionDataOfMulticall);
    EthCall send = web3j.ethCall(ethCallTransaction, DefaultBlockParameterName.PENDING).sendAsync().get();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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