Decoding the multicall function data input in PancakeSwap v3 / UniswapV3

Henry Tirla
2 min readJun 3, 2023

Decoding Ethereum transaction input data can be challenging, especially when the transaction is interacting with a complex smart contract system like PancakeSwap or Uniswap .

In this article, we will walk through a step-by-step approach to decoding the multicall function data input in PancakeSwap v3 transactions using web3.py, a Python library for interacting with Ethereum.

The Challenge

When examining the transaction data of a multicall function, one can easily be overwhelmed. The multicall function is a way to bundle together multiple function calls into a single transaction. This is efficient but can be challenging to decode as each function call within the multicall function is also encoded.

An initial attempt to decode the multicall function might produce something similar to this:

v3pcsContract = web3.eth.contract(address=v3pcsRouter, abi=v3pcsAbi)
decoded = v3pcsContract.decode_function_input(data)

This would Ouptut:

(<Function multicall(uint256,bytes[])>, {'deadline': 1685755714, 
'data': [b'Bq*g\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x10\xdaZ\xbb\xe0f\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x92\xcf\x81\x03Kg[\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\xca.\x86\xee\x1d\t:L9kr.\x17X}\x84^\x10\xd1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbbL\xdb\x9c\xbd6\xb0\x1b\xd1\xcb\xae\xbf-
\xe0\x8d\x91s\xbc\t\\\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d/\r\xa1i\xce\xb9
\xfc{1Db\x8d\xb1V\xf3\xf6\xc6\r\xbe', b'\x12!\x0e\x8a']})

While we can see the multicall function and its arguments, the 'data' argument, a byte array, still needs further decoding.

Unpacking the Mystery

The ‘data’ argument is a list of byte arrays. Each byte array represents a call to a different function, and they’re all bundled together in this one transaction by the multicall function. Each call is encoded in the same way as the original transaction data: the first 4 bytes represent the function signature, and the rest are the encoded arguments.

So, how do we unpack this?

First, let’s extract the ‘data’ from our decoded output:

byte_array = decoded[1]['data'][0]

The variable byte_array now contains the raw bytes of the first nested function call.

Now we can decode this nested function call:

nested_call_decoded = v3pcsContract.decode_function_input(byte_array)

nested_call_decoded now holds the function and its arguments for the first nested call.

To unpack all the nested calls, we can loop through the byte arrays like this:

for call_data in decoded[1]['data']:
nested_call_decoded = v3pcsContract.decode_function_input(call_data)
print(nested_call_decoded)

Note

One crucial point to consider is that you would need to know the correct ABI to use for these nested calls. It might be the same contract or a different one, depending on how the multicall function is being used.

It's important to note that understanding the accurate ABI for nested calls is crucial. The contract used may be the same or different, depending on the application of the multicall function. Decoding transactions can provide a wealth of information about how users interact with a smart contract, but it’s only one piece of the puzzle.

I hope this article has provided you with a clearer understanding of how to decode PancakeSwap v3 and Uniswapmulticall function data inputs. Keep exploring, keep learning, and happy coding!

Helpful Resources

For a more hands-on experience, I recommend checking out my repositories:

  1. Decoding multicall on Ethereum
  2. Decoding multicall on Binance Smart Chain

--

--