Skip to content

Decode_Transaction

Elnaril edited this page Nov 2, 2023 · 2 revisions

How to decode a transaction sent to the Universal Router ?

Let's say we want to decode this transaction: 0xe97c7d58078dff6fd3052914926a7efb769cd2e10932f35f9a48ceebcf48e02c

Codec creation

We can build the codec either with a rpc endpoint address or a valid Web3 instance.

With a rpc endpoint

from uniswap_universal_router_decoder import RouterCodec
rpc_endpoint="https://..."  # your rpc endpoint
codec = RouterCodec(rpc_endpoint=rpc_endpoint)

With a Web3 instance

from web3 import Web3
from uniswap_universal_router_decoder import RouterCodec
w3 = Web3(...)  # your web3 instance
codec = RouterCodec(w3=w3)

Codec usage

Now that the codec is instantiated, we can call its method decode.transaction() like that:

trx_hash = "0xe97c7d58078dff6fd3052914926a7efb769cd2e10932f35f9a48ceebcf48e02c"
decoded_transaction = codec.decode.transaction(trx_hash)

The decoder will get the transaction from the blockchain and decode it, along with its input data.

Result

Example of decoded transaction returned by the method method decode.transaction():

Full result

{'accessList': [],
 'blockHash': HexBytes('0x766e99704c37717c3a8d2ae27f9b9a04a1b962e685ff7347935fc54f0e24353d'),
 'blockNumber': 18254122,
 'chainId': 1,
 'from': '0x3ccb28ae637eB2e9D04b514FFeB1b9d694f91E08',
 'gas': 221803,
 'gasPrice': 6922978103,
 'hash': HexBytes('0xe97c7d58078dff6fd3052914926a7efb769cd2e10932f35f9a48ceebcf48e02c'),
 'input': HexBytes('0x3593564c000000000000000000 ... 0000000000'),
 'maxFeePerGas': 9491000000,
 'maxPriorityFeePerGas': 500000000,
 'nonce': 8,
 'r': HexBytes('0x94bab3213e85ad8539729bcf3eaadea78dcf2c3c13ede001d7ff1e0de48ce518'),
 's': HexBytes('0x11a86783aaa34ba3bcf140c9c85746a9062b5a1e9d26f8ffe29830ac51e851f0'),
 'to': '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD',
 'transactionIndex': 26,
 'type': 2,
 'v': 0,
 'value': 2871909791457383,
 'yParity': 0,
 'decoded_input': {'commands': b'\x0b\x01\x0c',
                   'inputs': [(<Function WRAP_ETH(address,uint256)>,
                               {'recipient': '0x0000000000000000000000000000000000000002',
                                'amountMin': 2871909791457383}),
                              (<Function V3_SWAP_EXACT_OUT(address,uint256,uint256,bytes,bool)>,
                               {'recipient': '0x0000000000000000000000000000000000000001',
                                'amountOut': 1020000000000000000,
                                'amountInMax': 2871909791457383,
                                'path': b'\x1f\x98@\xa8]Z\xf5\xbf\x1d\x17b\xf9'
                                        b'%\xbd\xad\xdcB\x01\xf9\x84'
                                        b"\x00'\x10\xc0*\xaa9\xb2#\xfe\x8d\n"
                                        b"\x0e\\O'\xea\xd9\x08<ul\xc2",
                                'payerIsSender': False}),
                              (<Function UNWRAP_WETH(address,uint256)>,
                               {'recipient': '0x0000000000000000000000000000000000000001',
                                'amountMin': 0})],
                   'deadline': 1696146155}}

For readability sake, the input field has been truncated. Notice the decoded_input field which contains the data sent to the Universal Router.

Zoom on the decoded input

{
'commands': b'\x0b\x01\x0c',  # the list of commands sent to the UR
'inputs': [  # the inputs used for each command
   (
     <Function WRAP_ETH(address,uint256)>,  # the function corresponding to the first command
     {                                      # and its parameters
          'recipient': '0x0000000000000000000000000000000000000002',  # code indicating the recipient of this command is the router
          'amountMin': 2871909791457383  # the amount in WEI to wrap
     }
   ),
   (
     <Function V3_SWAP_EXACT_OUT(address,uint256,uint256,bytes,bool)>,  # the function corresponding to the second command
     {                                                                  # and its parameters
          'recipient': '0x0000000000000000000000000000000000000001',  # code indicating the sender will receive the output of this command
          'amountOut': 1020000000000000000,  # the exact amount to receive
          'amountInMax': 2871909791457383,  # the max amount expected of the sold token for the swap to be executed 
          'path': b'\x1f\x98@\xa8]Z\xf5\xbf\x1d\x17b\xf9'  # the V3 path (tokens + pool fees)
                  b'%\xbd\xad\xdcB\x01\xf9\x84'  # can be decoded with the method decode.v3_path()
                  b"\x00'\x10\xc0*\xaa9\xb2#\xfe\x8d\n"
                  b"\x0e\\O'\xea\xd9\x08<ul\xc2",
          'payerIsSender': False  # a bool indicating if the input tokens come from the sender or are already in the UR
     }
   ),
   (
     <Function UNWRAP_WETH(address,uint256)>,  # the function corresponding to the third command
     {
       'recipient': '0x0000000000000000000000000000000000000001',  # code indicating the sender will receive the output of
       'amountMin': 0  # the min amount in WEI to unwrap => The sender will get back all WETH not used in the swap.
     }
   )
],
'deadline': 1696146155  # The deadline after which the transaction is not valid any more.
}

To get the function names:

decoded_input_data[0].fn_name  # "execute"
decoded_input_data[1]['inputs'][0][0].fn_name  # "WRAP_ETH"
decoded_input_data[1]['inputs'][1][0].fn_name  # "V3_SWAP_EXACT_OUT"
decoded_input_data[1]['inputs'][2][0].fn_name  # "UNWRAP_WETH"
Clone this wiki locally