Search code examples
solidity

Failed to call solidity function with array argument


I'm trying to execute contract's function use abi encode and call function, but I'm getting an error. Don't know how to resolve this issue.

pragma solidity >=0.8.0;

contract AbiTest {

    event A(uint);
    event Out(bytes);

    function test() public {
        bytes4 func = bytes4(keccak256("callMe(uint[])"));

        uint[] memory arr = new uint[](3);
        arr[0] = 3;
        arr[1] = 4;

        (bool res, bytes memory data) = address(this).call(abi.encode(func, arr));
        emit Out(data);
        require(res);
    }

    function callMe(uint[] memory array) public {
            emit A(array[0]);
            emit A(array[1]);
    }
}

This solution works well, if arguments sizes are known at compile time, but with dynamic sizes this doesn't work.

How can I resolve it?


Solution

  • Need to use abi.encodePacked see Solidity doc instead of abi.encode because second one don't compress items and therefore uses 32 bytes for the function name.

    pragma solidity >=0.8.0;
    
    contract Abi2Test {
    
        function getByte() public returns (bytes memory) {
            bytes4 func = bytes4(keccak256("callMe(uint256[])"));
            return abi.encode(func);
         }
    
        function getByte2() public returns (bytes memory) {
            bytes4 func = bytes4(keccak256("callMe(uint256[])"));
            return abi.encodePacked(func);
         }
    }
    

    getByte output: 0x6600981c00000000000000000000000000000000000000000000000000000000

    getByte2 output: 0x6600981c

    Therefore, when you use encode with function signature additionally will be allocated 28 bytes and EVM can't correctly parse passed value.

    So, to correctly call function with abi.encode and abi.encodePacked you need to pack arguments with encode at the first, and at the second group function's signature with encoded bytes.

    pragma solidity >=0.8.0;
    
    contract Abi2Test {
    
        event A(uint256);
        event Out(bytes);
        event Out1(bytes);
    
        function test() public {
            bytes4 func = bytes4(keccak256("callMe(uint256[])"));
    
            uint256[] memory arr = new uint256[](3);
            arr[0] = 3;
            arr[1] = 4;
    
    
            (bool res, bytes memory data) = address(this).call(abi.encodePacked(func, abi.encode(arr)));
            emit Out(data);
            require(res);
        }
    
        function callMe(uint256[] memory array) public {
                emit A(array.length);
                emit Out1(msg.data);
        }
    }