Uniswap has recently rolled out a new Universal router (blog here: https://docs.uniswap.org/contracts/universal-router/technical-reference)
I've decoded a transaction using the Uniswap ABI and ethersjs parseTransaction into something like so:
TransactionDescription {
args: [
'0x0b00',
[
'0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000009184e72a000',
'0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000'
],
BigNumber { _hex: '0x63c87f23', _isBigNumber: true },
commands: '0x0b00',
inputs: [
'0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000009184e72a000',
'0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000'
],
deadline: BigNumber { _hex: '0x63c87f23', _isBigNumber: true }
],
functionFragment: {
type: 'function',
name: 'execute',
constant: false,
inputs: [ [ParamType], [ParamType], [ParamType] ],
outputs: [],
payable: true,
stateMutability: 'payable',
gas: null,
_isFragment: true,
constructor: [Function: FunctionFragment] {
from: [Function (anonymous)],
fromObject: [Function (anonymous)],
fromString: [Function (anonymous)],
isFunctionFragment: [Function (anonymous)]
},
format: [Function (anonymous)]
},
name: 'execute',
signature: 'execute(bytes,bytes[],uint256)',
sighash: '0x3593564c',
value: BigNumber {
_hex: '0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000063c87f2300000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000',
_isBigNumber: true
}
}
However, I can not understand how I could decode the inputs into the correct values.
The technical reference suggests that the bytes array for input should have the following things in it:
The second parameter for the function is an array of bytes strings. Each element in the array is the abi-encoded input that will be used for the respective command.
V3_SWAP_EXACT_IN
address The recipient of the output of the trade
uint256 The amount of input tokens for the trade
uint256 The minimum amount of output tokens the user wants
bytes The UniswapV3 encoded path to trade along
bool A flag for whether the input tokens should come from the msg.sender (through Permit2) or whether the funds are already in the UniversalRouter
However, the bytes array only has length 2:
inputs: [
'0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000009184e72a000',
'0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000'
],
What am I misunderstanding here, and how can I decode a universal router transaction
Best Answer
The misunderstanding seems to come from the inputs.
You can see that in the object you mentioned there are these lines:
Where the commands (Commands.sol) is interpreted as:
That is why you see 2 elements in the inputs array. The first element is the inputs corresponding to the first function (WRAP_ETH) The second element in the array are the inputs corresponding to V3_SWAP_EXACT_IN
Let's look at the first function for example and decode that.![enter image description here](https://i.stack.imgur.com/1FhZU.png)
Using this same method you can do the same thing for V3_SWAP_EXACT_IN.
Note there might be tools to decode these by default if you have the ABI, but this is why you see 2 inputs only.