web3j icon indicating copy to clipboard operation
web3j copied to clipboard

FunctionEncoder.buildMethodSignature generates incorrect signature

Open jimmyneutront opened this issue 4 years ago • 2 comments

FunctionEncoder.buildMethodSignature generates incorrect signature

When building a signature for a method that accepts dynamic arrays, buildMethodSignature includes the string literal dynamicarray in the signature instead of the correct parameter type, such as bytes[].

Steps To Reproduce

Consider the following Solidity struct:

struct Offer {
        bool isCreated;
        bool isTaken;
        address maker;
        bytes interfaceId;
        address stablecoin;
        uint256 amountLowerBound;
        uint256 amountUpperBound;
        uint256 securityDepositAmount;
        SwapDirection direction;
        bytes price;
        bytes[] settlementMethods;
        uint256 protocolVersion;
        bytes32 extraData;
}

and consider the following Solidity function:

function openOffer(bytes16 offerID, Offer memory newOffer) public {
        //Actual function implementation not relevant
}

Create a basic contract containing this struct and function, and use Web3J to generate a wrapper for it. Deploy the contract on a test blockchain, and then try to call the openOffer method of the generated contract wrapper.

Expected behavior

The function call is successful.

Actual behavior

The function call reverts without giving a reason string.

Environment

  • Web3j version: Recent clone from master branch
  • Java Version (not Android): Temurin 11 LTS (jdk-11.0.13+8 for macOS x64 Intel)
  • Operating System: macOS 12.0.1 Monterey

Additional context

In the process of calling openOffer, buildMethodSignature generates the following string to be hashed in order to obtain the method signature:

openOffer(bytes16(bool,bool,address,bytes,address,uint256,uint256,uint256,uint8,bytes,dynamicarray,uint256,bytes32))

which produces the following method signature: 5c3952db. This is obviously wrong, as Solidity has no type named dynamicarray, and thus the function call will revert. The correct method signature is 23cd78c0, and this can be obtained by replacing dynamicarray in the previous string with bytes[] to produce the string:

openOffer(bytes16,(bool,bool,address,bytes,address,uint256,uint256,uint256,uint8,bytes,bytes[],uint256,bytes32))

When buildMethodSignature is replaced with the following:

protected static String buildMethodSignature(
            final String methodName, final List<Type> parameters) {
        if (methodName == "openOffer") {
            return "openOffer(bytes16,(bool,bool,address,bytes,address,uint256,uint256,uint256,uint8,bytes,bytes[],uint256,bytes32))";
        }
        final StringBuilder result = new StringBuilder();
        result.append(methodName);
        result.append("(");
        final String params =
                parameters.stream().map(Type::getTypeAsString).collect(Collectors.joining(","));
        result.append(params);
        result.append(")");
        return result.toString();
}

the function call succeeds.

jimmyneutront avatar Dec 26 '21 23:12 jimmyneutront

Hi everyone.I encountered the same problem with version 4.8.6. I rewrote part of the code to make sure it works properly, I put the modified code below, hope it can help someone who needs it.

public class MyDynamicStruct extends DynamicStruct {
    public MyDynamicStruct(List<Type> values) {
        super(values);
    }
    public MyDynamicStruct(Type... values) {
        super(values);
    }
    public MyDynamicStruct(Class<Type> type, Type... values) {
        super(type, values);
    }

    @Override
    @SneakyThrows
    public String getTypeAsString() {
        final StringBuilder type = new StringBuilder("(");
        Field itemTypesField = DynamicStruct.class.getDeclaredField("itemTypes");
        itemTypesField.setAccessible(true);
        List<Class<Type>> itemTypes = (List<Class<Type>>) itemTypesField.get(this);
        for (int i = 0; i < itemTypes.size(); ++i) {
            final Class<Type> cls = itemTypes.get(i);
            if (StructType.class.isAssignableFrom(cls)) {
                type.append(getValue().get(i).getTypeAsString());
            } else if (Array.class.isAssignableFrom(cls)) {
                type.append(getValue().get(i).getTypeAsString());
            } else {
                type.append(AbiTypes.getTypeAString(cls));
            }
            if (i < itemTypes.size() - 1) {
                type.append(",");
            }
        }
        type.append(")");
        return type.toString();
    }
}

Finally, replace the "DynamicStruct" in the swapper class and you're good to go. Maybe you also need to modify the DynamicArray link

mmotiy avatar Feb 23 '22 12:02 mmotiy

I faced this issue too,when will offical fix and publish a new version?

tmac2yt avatar May 10 '22 10:05 tmac2yt

Solved after merging #1526.

gtebrean avatar Mar 08 '23 13:03 gtebrean