Solidity Uniswap – How to Decode Multicall Bytes into Readable Format

bytesmethodsolidityuniswap

I created a Uniswap V3 pool using UI. In Transaction multicall(bytes[] data) function was called with this parameter:

[0]:  0000000000000000000000000000000000000000000000000000000000000020
[1]:  0000000000000000000000000000000000000000000000000000000000000002
[2]:  0000000000000000000000000000000000000000000000000000000000000040
[3]:  0000000000000000000000000000000000000000000000000000000000000100
[4]:  0000000000000000000000000000000000000000000000000000000000000084
[5]:  13ead5620000000000000000000000005c8cd1c2f2997f7a041026cc29de8177
[6]:  b4c6d8ec00000000000000000000000089e54f174ca5ff39cf53ab58004158e2
[7]:  ca012eac00000000000000000000000000000000000000000000000000000000
[8]:  00000bb8000000000000000000000000000000000035f2482336c0d4c2ba6e94
[9]:  faa1d66f00000000000000000000000000000000000000000000000000000000
[10]: 0000000000000000000000000000000000000000000000000000000000000164
[11]: 883164560000000000000000000000005c8cd1c2f2997f7a041026cc29de8177
[12]: b4c6d8ec00000000000000000000000089e54f174ca5ff39cf53ab58004158e2
[13]: ca012eac00000000000000000000000000000000000000000000000000000000
[14]: 00000bb8ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
[15]: fff2764c00000000000000000000000000000000000000000000000000000000
[16]: 000a11a8000000000000000000000000000000000000000000000000000000e8
[17]: d4a510000000000000000000000000000000000000000000000a56d35c029fd1
[18]: 6645e079000000000000000000000000000000000000000000000000000000e8
[19]: 40308c030000000000000000000000000000000000000000000a503344abc0fb
[20]: e23670910000000000000000000000005a2b5cb4ce921abd65f0c66c2c839894
[21]: bfc2076c00000000000000000000000000000000000000000000000000000000
[22]: 6244356a00000000000000000000000000000000000000000000000000000000

This is how multicall function looks like:

function multicall(bytes[] calldata data) external payable override returns (bytes[] memory results) {
    results = new bytes[](data.length);
    for (uint256 i = 0; i < data.length; i++) {
        (bool success, bytes memory result) = address(this).delegatecall(data[i]);

        if (!success) {
            // Next 5 lines from https://ethereum.stackexchange.com/a/83577
            if (result.length < 68) revert();
            assembly {
                result := add(result, 0x04)
            }
            revert(abi.decode(result, (string)));
        }

        results[i] = result;
    }
}

How to understand what methods (and parameters) were called here:

address(this).delegatecall(data[i])

Best Answer

The comments for the accepted answer includes a link to an NPM library that makes an http call to get the function name - which is not a good idea. Also, I think it's much more complicated than it needs to be. The Ethers library provides most of the functionality needed.

In the case of Uniswap multicalls, the contract calls the function on the current contract, so there really is no need to try to look it up from any contract - the function will be in the contract. If you know the contract, then you can decode it using the ABI. You must know the ABI because you decoded the multicall.

const decodeMulticall = (abi: ReadonlyArray<any>, calls: string[]) => {
  const abiInterface = new ethers.utils.Interface(abi);
  return calls.map(call => {
    try {
      const func = call.slice(0, 10);
      const decodedArgs = abiInterface.decodeFunctionData(func, call)
      const functionName = abiInterface.getFunction(func).name
      return {name: functionName, args: decodedArgs};
    }
    catch (ex) {
      return; // you could return a type here to indicate it was not parsed
    }
  })