ABI Encoder – abi.decode with Experimental ABIEncoderV2 Reverts with Nested Arrays

abiabiencoderv2decodingrevert-opcode

I have a function with a parameter of bytes calldata that needs to be decoded to a struct, but the call reverts in assembly. I believe the issue has something to do with the nested array. I have used a similiar technique with only a 1-dimensional array and it seems to work OK.

struct InputParams {
    address[] exchangeAddresses;
    address[][] exchangePoolOrPaths;
    address[] tokenAddresses;
    uint256 tokenStartAmount;
    uint256[] tokenAmountsExpected;
}

function execute(uint blockNumber, bytes calldata data, bool admin) external
{
    InputParams memory params = abi.decode(data, (InputParams));  // this reverts in assembly
}

Inspecting the raw input to transacting with this function does not provide much info. I parsed out below the separate parameters and they seem to be encoded OK:

        0xb83775a0  // function signature
        0000000000000000000000000000000000000000000000000000000000000000  // parameter "blockNumber" == 0
        0000000000000000000000000000000000000000000000000000000000000060  // parameter bytes calldata "data" offset value
        0000000000000000000000000000000000000000000000000000000000000001  // parameter "admin" == 1
        00000000000000000000000000000000000000000000000000000000000002c0  // struct InputParams length 
        00000000000000000000000000000000000000000000000000000000000000a0  // offset of exchangeAddresses[]
        0000000000000000000000000000000000000000000000000000000000000100  // offset of exchangePoolOrPaths[][]
        00000000000000000000000000000000000000000000000000000000000001e0  // offset of tokenAddresses[]
        0000000000000000000000000000000000000000000000004563918244f40000  // value of tokenStartAmount
        0000000000000000000000000000000000000000000000000000000000000260  // offset of tokenAmountsExpected[]
        0000000000000000000000000000000000000000000000000000000000000002  // number of items in exchangeAddresses[]
        0000000000000000000000001cb3aca0179bc96cd9612d6c27285b0458a2500f  // value of exchangeAddresses[0]
        00000000000000000000000080d73310b92da08b230adf441a6352e8f9631a77  // value of exchangeAddresses[1]
        0000000000000000000000000000000000000000000000000000000000000002  // number of items in exchangePoolOrPaths[*][]
        0000000000000000000000000000000000000000000000000000000000000040  // offset of array at exchangePoolOrPaths[0]
        0000000000000000000000000000000000000000000000000000000000000080  // offset of array at exchangePoolOrPaths[1]
        0000000000000000000000000000000000000000000000000000000000000001  // number of items in array at exchangePoolOrPaths[0]
        000000000000000000000000b5ffd0a221fc94de9255382e70ce1aec76b68c89  // value of exchangePoolOrPaths[0][0]
        0000000000000000000000000000000000000000000000000000000000000001  // number of items in array at exchangePoolOrPaths[1]
        0000000000000000000000003b3d4eefdc603b232907a7f3d0ed1eea5c62b5f7  // value of exchangePoolOrPaths[1][0]
        0000000000000000000000000000000000000000000000000000000000000003  // number of items in tokenAddresses[]
        000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2  // value of tokenAddresses[0]
        0000000000000000000000000ae055097c6d159879521c384f1d2123d1f195e6  // value of tokenAddresses[1]
        000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2  // value of tokenAddresses[2]
        0000000000000000000000000000000000000000000000000000000000000002  // number of items in tokenAmountsExpected[]
        0000000000000000000000000000000000000000000000039fe550f187a54000  // value of tokenAmountsExpected[0]
        0000000000000000000000000000000000000000000000004563918244f40000  // value of tokenAmountsExpected[1]

Does anyone have any idea what the problem could be here?

Edit:

I think I simplified the problem case. I think there's an issue with decode and dynamic arrays inside structs.

What works:

    struct InputParams
    {
        address[2] exchangeAddresses;
    }

    function execute(uint blockNumber, bytes calldata data, bool admin) external
    {
        InputParams memory params = abi.decode(data, (InputParams));
    }

with calldata:

0xb83775a0
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000040
000000000000000000000000b5ffd0a221fc94de9255382e70ce1aec76b68c89
0000000000000000000000003b3d4eefdc603b232907a7f3d0ed1eea5c62b5f7

What does not work (reverts during decode):

    struct InputParams
    {
        address[] exchangeAddresses;
    }

    function execute(uint blockNumber, bytes calldata data, bool admin) external
    {
        InputParams memory params = abi.decode(data, (InputParams));
    }

with calldata:

0xb83775a0
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
000000000000000000000000b5ffd0a221fc94de9255382e70ce1aec76b68c89
0000000000000000000000003b3d4eefdc603b232907a7f3d0ed1eea5c62b5f7

Does anyone have any ideas or if I am actually just passing incorrect calldata…

Best Answer

Issue resolved here: https://github.com/ethereum/solidity/issues/9747#issuecomment-687833241

The input calldata was incorrect

Related Topic