ethereumsoliditytruffleganacheevm

abi.encode() giving different results in ganache and sepolia , both of them don't make sense


Steps to reproduce -

  1. Deploy to Ganache and sepolia the following contract -
contract ABIExample {
    
    event EncodedData(bytes encodedData);

    function encodeAndDecode(uint a, uint[] memory b, bytes memory c) public  returns (uint, uint[] memory, bytes memory) {
        // abi.encode example
        bytes memory encodedData = abi.encode(a, b, c);
        emit EncodedData(encodedData);
        
        // abi.decode example
        (uint decodedA, uint[] memory decodedB, bytes memory decodedC) = abi.decode(encodedData, (uint, uint[], bytes));
        
        return (decodedA, decodedB, decodedC);
        //return (a,b,c)
    }

    function encodePackedExample(uint a, uint b) public  returns (bytes memory) {
        // abi.encodePacked example types shorter than 32 bytes are concatenated directly, without padding or sign extension
        bytes memory packedData = abi.encodePacked(a, b);
        emit EncodedData(packedData);
        
        return packedData;
    }

    function encodeWithSelectorExample(uint a, uint b) public  returns (bytes memory) {
        // abi.encodeWithSelector example
        bytes4 selector = bytes4(keccak256("exampleFunction(uint256,uint256)"));
        bytes memory withSelectorData = abi.encodeWithSelector(selector, a, b);
        emit EncodedData(withSelectorData);
        
        return withSelectorData;
    }

    function encodeCallExample() public  returns (bytes memory) {
        // abi.encodeCall example
        bytes memory callData = abi.encodeCall(this.exampleFunction, (42, 123));
        emit EncodedData(callData);
        
        return callData;
    }

    function exampleFunction(uint a, uint b) public pure returns (uint) {
        // Example function for abi.encodeCall
        return a + b;
    }
}
  1. Call encodeAndDecode(uint a, uint[] memory b, bytes memory c) with following parameters 1, [6,8,1,3,5],0xff.

  2. Observe the emitted event

Sepolia EncodedData event -

000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001ff00000000000000000000000000000000000000000000000000000000000000

https://sepolia.etherscan.io/address/0x9bf331503430077a1edd0434c8c79d72a2066cf8#events

Ganache EncodedData event -

0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001ff00000000000000000000000000000000000000000000000000000000000000

What it should be if my understanding is correct

<32bytes for uint(1)> <32 bytes representing length of array> <5*32 bytes for array elements> <32bytes for 0xff> So-

00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000005ff00000000000000000000000000000000000000000000000000000000000000

Why am I getting different results for Ganache and Sepolia . Why are they not matching with my results ?


Solution

  • The Sepolia result (you can check here) is the same as your Ganache result. The hex version of it (which is similar, but not the same as the one you provided) has some prefixes (for the total length), but still contains the result from Ganache.

    Here's the breakdown

    0000000000000000000000000000000000000000000000000000000000000001 - 1st input
    0000000000000000000000000000000000000000000000000000000000000060 - 2nd input
    0000000000000000000000000000000000000000000000000000000000000120 - 3rd input
    
    0000000000000000000000000000000000000000000000000000000000000005
    0000000000000000000000000000000000000000000000000000000000000006
    0000000000000000000000000000000000000000000000000000000000000008
    0000000000000000000000000000000000000000000000000000000000000001
    0000000000000000000000000000000000000000000000000000000000000003
    0000000000000000000000000000000000000000000000000000000000000005
    
    0000000000000000000000000000000000000000000000000000000000000001
    ff00000000000000000000000000000000000000000000000000000000000000
    

    Since your 2nd input has a dynamic length, it will be represented as an offset. Here, 60 in hex equals to 96 bytes. Skip 3 lines (3 x 32 bytes) from the beginning and we got 5, which is the length of the array.

    Same for the 3rd input, 120 in hex equals to 288 bytes (9 x 32 bytes). Skip 9 lines and we got the final input.