web3j icon indicating copy to clipboard operation
web3j copied to clipboard

Method Id is not calculated correctly if a parameter is a DynamicArray within a DynamicStruct

Open daniellehrner opened this issue 4 years ago • 4 comments

Method Id is not calculated correctly if a parameter is a DynamicArray within a DynamicStruct

In Solidity it is possible to define an array within a struct and use it as method parameter:

    struct StructWithArray {
        uint256[] numbers;
    }
    
    function functionName(StructWithArray calldata _parameter) external;

If such a construct is used, web3j does not calculate the method id correctly. If a transaction is sent to the smart contract, it will fail, because the method id is wrong and therfore unknown to the contract.

Steps To Reproduce

Define a function which has as parameter a DynamicArray within a DynamicStruct. Calculate the method id with FunctionEncoder.buildMethodSignature. I have prepared a test to reproduce this:

git clone https://github.com/daniellehrner/web3j.git
git checkout method_id_array_in_struct
./gradlew abi:test --tests "org.web3j.abi.DefaultFunctionEncoderTest.testArrayWithinStructEncode"

Expected behavior

The created test should be successful

Actual behavior

The created test fails

@Ferparishuertas

daniellehrner avatar Oct 20 '21 13:10 daniellehrner

@daniellehrner Thanks for reporting this I will look into it.

AlexandrouR avatar Oct 22 '21 12:10 AlexandrouR

same question

kongkongye avatar Oct 23 '21 00:10 kongkongye

I'm seeing a similar problem with arrays of Structs:


struct Foo {
    address a;
    uint64 b;
}

function doStuff(Foo[] calldata aFoos, Foo[] calldata bFoos) external {
    ...
}

mahileeb avatar Feb 09 '22 01:02 mahileeb

Turns out my issue is due to empty array parameters as per the source code comment in https://github.com/web3j/web3j/blob/master/abi/src/main/java/org/web3j/abi/datatypes/DynamicArray.java:

    public String getTypeAsString() {
        String type;
        // Handle dynamic array of zero length. This will fail if the dynamic array
        // is an array of structs.
        if (value.isEmpty()) {
            type = AbiTypes.getTypeAString(getComponentType());
        } else {
            if (StructType.class.isAssignableFrom(value.get(0).getClass())) {
                type = value.get(0).getTypeAsString();
            } else {
                type = AbiTypes.getTypeAString(getComponentType());
            }
        }
        return type + "[]";
    }

Guess I'll just have to pass in some zero values as a workaround

mahileeb avatar Feb 09 '22 01:02 mahileeb

this was fixed with the PR mentioned in #1742

gtebrean avatar Mar 02 '23 19:03 gtebrean