Decoding the multicall
function data input in PancakeSwap v3 / UniswapV3
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: