fix: each router release names and reads its own commands (closes #592)
Each Universal Router release now carries the command it runs for each command type, by the name its pinned Commands.sol gives it, and the decoder names and reads each step as that command instead of using one table for every router. On UniversalRouterV1 and UniversalRouterV1_2_V2Support, 0x10 to 0x13 are NFT commands, such as Seaport: they are no longer read as V4 commands, so they draw no swap lines and their payouts count as unread. UniversalRouterV1 has no BALANCE_CHECK_ERC20 and reverts on 0x0e, which is now listed as Command 0x0e and sets no minimum. No release lets a V4 swap fail, so the zero-minimum reading for a flagged one is gone. Model: opus-5-5
This commit was merged in pull request #616.
This commit is contained in:
@@ -2983,15 +2983,25 @@ Currently supported:
|
|||||||
in the parameter shape deployed on mainnet and Sepolia, not in the newer one
|
in the parameter shape deployed on mainnet and Sepolia, not in the newer one
|
||||||
on `v4-periphery` `main`, which adds a `minHopPriceX36` field. An action in
|
on `v4-periphery` `main`, which adds a `minHopPriceX36` field. An action in
|
||||||
any other shape is not read, and its step is listed as
|
any other shape is not read, and its step is listed as
|
||||||
`V4 Swap (could not be decoded)`. Each step is named by its command type,
|
`V4 Swap (could not be decoded)`. Each step is named and read as the command
|
||||||
which the decoder reads from the command byte with the mask the router at that
|
that the release of the router at that address runs for its command type, as
|
||||||
address uses: `0x1f` for `UniversalRouterV1`, `0x3f` for
|
that release's `contracts/libraries/Commands.sol` names it. The decoder reads
|
||||||
`UniversalRouterV1_2_V2Support` and `UniversalRouterV2`, and `0x7f` for
|
the type from the command byte with the mask that router uses: `0x1f` for
|
||||||
`UniversalRouterV2_1_1` and `UniversalRouterV2_1_2`. The allow-revert flag
|
`UniversalRouterV1`, `0x3f` for `UniversalRouterV1_2_V2Support` and
|
||||||
(`0x80`) therefore does not change the command a step is read as. The router
|
`UniversalRouterV2`, and `0x7f` for `UniversalRouterV2_1_1` and
|
||||||
reverts the whole transaction when a step fails, unless the step's command
|
`UniversalRouterV2_1_2`. The allow-revert flag (`0x80`) therefore does not
|
||||||
byte has that flag set and its command is one the router lets fail, as each
|
change the command a step is read as. Releases run different commands for some
|
||||||
release's `contracts/base/Dispatcher.sol` shows: on `UniversalRouterV2`,
|
types: on `UniversalRouterV1` and `UniversalRouterV1_2_V2Support`, `0x10` to
|
||||||
|
`0x13` are NFT commands, where the later routers run `V4_SWAP`,
|
||||||
|
`V3_POSITION_MANAGER_PERMIT`, `V3_POSITION_MANAGER_CALL` and
|
||||||
|
`V4_INITIALIZE_POOL`. The decoder does not read those routers' NFT commands,
|
||||||
|
and lists each by its name there, for example `Seaport` for `SEAPORT` (`0x10`
|
||||||
|
on `UniversalRouterV1`). A type a release names no command for is listed by
|
||||||
|
its number, for example `Command 0x0e` on `UniversalRouterV1`, which has no
|
||||||
|
`BALANCE_CHECK_ERC20` and reverts on it. The router reverts the whole
|
||||||
|
transaction when a step fails, unless the step's command byte has that flag
|
||||||
|
set and its command is one the router lets fail, as each release's
|
||||||
|
`contracts/base/Dispatcher.sol` shows: on `UniversalRouterV2`,
|
||||||
`UniversalRouterV2_1_1` and `UniversalRouterV2_1_2`, `PERMIT2_PERMIT`,
|
`UniversalRouterV2_1_1` and `UniversalRouterV2_1_2`, `PERMIT2_PERMIT`,
|
||||||
`PERMIT2_PERMIT_BATCH`, `BALANCE_CHECK_ERC20`, `V3_POSITION_MANAGER_PERMIT`,
|
`PERMIT2_PERMIT_BATCH`, `BALANCE_CHECK_ERC20`, `V3_POSITION_MANAGER_PERMIT`,
|
||||||
`V3_POSITION_MANAGER_CALL`, `V4_INITIALIZE_POOL`, `V4_POSITION_MANAGER_CALL`
|
`V3_POSITION_MANAGER_CALL`, `V4_INITIALIZE_POOL`, `V4_POSITION_MANAGER_CALL`
|
||||||
@@ -3004,11 +3014,7 @@ Currently supported:
|
|||||||
V3 and V4 swap and each payment of ETH or an ERC-20 token, the flag has no
|
V3 and V4 swap and each payment of ETH or an ERC-20 token, the flag has no
|
||||||
effect: the step succeeds or the whole transaction reverts, so the step reads
|
effect: the step succeeds or the whole transaction reverts, so the step reads
|
||||||
as it does without the flag. A `BALANCE_CHECK_ERC20` the router lets fail
|
as it does without the flag. A `BALANCE_CHECK_ERC20` the router lets fail
|
||||||
guarantees nothing, so it is read as one whose `minBalance` is zero. On
|
guarantees nothing, so it is read as one whose `minBalance` is zero. Only the
|
||||||
`UniversalRouterV1` and `UniversalRouterV1_2_V2Support`, `0x10` is an NFT
|
|
||||||
market call the router lets fail, which the decoder still reads as a V4 swap
|
|
||||||
([#592](https://git.eeqj.de/sneak/AutistMask/issues/592)); with the flag set,
|
|
||||||
a minimum it states is read as zero: `None (no minimum guaranteed)`. Only the
|
|
||||||
routers with the `0x7f` mask have `PAY_PORTION_FULL_PRECISION` (`0x07`),
|
routers with the `0x7f` mask have `PAY_PORTION_FULL_PRECISION` (`0x07`),
|
||||||
listed as `Pay Portion (Full Precision)`, and `ACROSS_V4_DEPOSIT_V3` (`0x40`),
|
listed as `Pay Portion (Full Precision)`, and `ACROSS_V4_DEPOSIT_V3` (`0x40`),
|
||||||
listed as `Across Bridge Deposit`; the other routers revert on `0x07`, and it
|
listed as `Across Bridge Deposit`; the other routers revert on `0x07`, and it
|
||||||
@@ -3083,7 +3089,8 @@ Currently supported:
|
|||||||
`V3_SWAP_EXACT_IN`, `V4_SWAP`, `SWEEP`, `TRANSFER`, `PAY_PORTION`,
|
`V3_SWAP_EXACT_IN`, `V4_SWAP`, `SWEEP`, `TRANSFER`, `PAY_PORTION`,
|
||||||
`PAY_PORTION_FULL_PRECISION`, `ACROSS_V4_DEPOSIT_V3`, `WRAP_ETH`,
|
`PAY_PORTION_FULL_PRECISION`, `ACROSS_V4_DEPOSIT_V3`, `WRAP_ETH`,
|
||||||
`UNWRAP_WETH` and `BALANCE_CHECK_ERC20` (`V3_SWAP_EXACT_OUT`,
|
`UNWRAP_WETH` and `BALANCE_CHECK_ERC20` (`V3_SWAP_EXACT_OUT`,
|
||||||
`PERMIT2_TRANSFER_FROM_BATCH` and `EXECUTE_SUB_PLAN` among them).
|
`PERMIT2_TRANSFER_FROM_BATCH`, `EXECUTE_SUB_PLAN` and the NFT commands among
|
||||||
|
them), or a type its release names no command for.
|
||||||
|
|
||||||
### Non-Goals Forever
|
### Non-Goals Forever
|
||||||
|
|
||||||
|
|||||||
+175
-71
@@ -24,8 +24,8 @@ const ROUTER_IFACE = new Interface([
|
|||||||
"function execute(bytes commands, bytes[] inputs, uint256 deadline)",
|
"function execute(bytes commands, bytes[] inputs, uint256 deadline)",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// The Universal Router releases deployed at the addresses below, each with two
|
// The Universal Router releases deployed at the addresses below, each with
|
||||||
// things read from its source at the commit linked for it:
|
// three things read from its source at the commit linked for it:
|
||||||
//
|
//
|
||||||
// - `commandTypeMask`, the mask it reads a command's type with: its dispatcher
|
// - `commandTypeMask`, the mask it reads a command's type with: its dispatcher
|
||||||
// runs `commandType & Commands.COMMAND_TYPE_MASK`, and the bits above the
|
// runs `commandType & Commands.COMMAND_TYPE_MASK`, and the bits above the
|
||||||
@@ -33,6 +33,11 @@ const ROUTER_IFACE = new Interface([
|
|||||||
// releases. Each is `COMMAND_TYPE_MASK` in the release's
|
// releases. Each is `COMMAND_TYPE_MASK` in the release's
|
||||||
// `contracts/libraries/Commands.sol`, the same file as in the source
|
// `contracts/libraries/Commands.sol`, the same file as in the source
|
||||||
// verified for its addresses.
|
// verified for its addresses.
|
||||||
|
// - `commands`, the command it runs for each command type, by the name that
|
||||||
|
// file gives it. Releases give some types different commands. A type the
|
||||||
|
// file names no command for, or only a placeholder, is left out: the router
|
||||||
|
// reverts on it, except UniversalRouterV1_2_V2Support on 0x14, which does
|
||||||
|
// nothing. decode() names and reads each step as its release's command.
|
||||||
// - `allowRevert`, the command types it lets fail without reverting the
|
// - `allowRevert`, the command types it lets fail without reverting the
|
||||||
// transaction. Its `execute()`, in `contracts/UniversalRouter.sol`, runs each
|
// transaction. Its `execute()`, in `contracts/UniversalRouter.sol`, runs each
|
||||||
// step through `dispatch()`, in `contracts/base/Dispatcher.sol`, and reverts
|
// step through `dispatch()`, in `contracts/base/Dispatcher.sol`, and reverts
|
||||||
@@ -46,6 +51,35 @@ const ROUTER_IFACE = new Interface([
|
|||||||
// UniversalRouterV1: https://github.com/Uniswap/universal-router/tree/cda697da221e24ca143bc4f55023694a2a16796f/contracts
|
// UniversalRouterV1: https://github.com/Uniswap/universal-router/tree/cda697da221e24ca143bc4f55023694a2a16796f/contracts
|
||||||
const ROUTER_V1 = {
|
const ROUTER_V1 = {
|
||||||
commandTypeMask: 0x1f,
|
commandTypeMask: 0x1f,
|
||||||
|
commands: {
|
||||||
|
0x00: "V3_SWAP_EXACT_IN",
|
||||||
|
0x01: "V3_SWAP_EXACT_OUT",
|
||||||
|
0x02: "PERMIT2_TRANSFER_FROM",
|
||||||
|
0x03: "PERMIT2_PERMIT_BATCH",
|
||||||
|
0x04: "SWEEP",
|
||||||
|
0x05: "TRANSFER",
|
||||||
|
0x06: "PAY_PORTION",
|
||||||
|
0x08: "V2_SWAP_EXACT_IN",
|
||||||
|
0x09: "V2_SWAP_EXACT_OUT",
|
||||||
|
0x0a: "PERMIT2_PERMIT",
|
||||||
|
0x0b: "WRAP_ETH",
|
||||||
|
0x0c: "UNWRAP_WETH",
|
||||||
|
0x0d: "PERMIT2_TRANSFER_FROM_BATCH",
|
||||||
|
0x10: "SEAPORT",
|
||||||
|
0x11: "LOOKS_RARE_721",
|
||||||
|
0x12: "NFTX",
|
||||||
|
0x13: "CRYPTOPUNKS",
|
||||||
|
0x14: "LOOKS_RARE_1155",
|
||||||
|
0x15: "OWNER_CHECK_721",
|
||||||
|
0x16: "OWNER_CHECK_1155",
|
||||||
|
0x17: "SWEEP_ERC721",
|
||||||
|
0x18: "X2Y2_721",
|
||||||
|
0x19: "SUDOSWAP",
|
||||||
|
0x1a: "NFT20",
|
||||||
|
0x1b: "X2Y2_1155",
|
||||||
|
0x1c: "FOUNDATION",
|
||||||
|
0x1d: "SWEEP_ERC1155",
|
||||||
|
},
|
||||||
// Its NFT market calls (0x10 to 0x14, 0x18 to 0x1c) and NFT owner checks
|
// Its NFT market calls (0x10 to 0x14, 0x18 to 0x1c) and NFT owner checks
|
||||||
// (0x15, 0x16).
|
// (0x15, 0x16).
|
||||||
allowRevert: [
|
allowRevert: [
|
||||||
@@ -55,6 +89,39 @@ const ROUTER_V1 = {
|
|||||||
// UniversalRouterV1_2_V2Support: https://github.com/Uniswap/universal-router/tree/41183d6eb154f0ab0e74a0e911a5ef9ea51fc4bd/contracts
|
// UniversalRouterV1_2_V2Support: https://github.com/Uniswap/universal-router/tree/41183d6eb154f0ab0e74a0e911a5ef9ea51fc4bd/contracts
|
||||||
const ROUTER_V1_2 = {
|
const ROUTER_V1_2 = {
|
||||||
commandTypeMask: 0x3f,
|
commandTypeMask: 0x3f,
|
||||||
|
commands: {
|
||||||
|
0x00: "V3_SWAP_EXACT_IN",
|
||||||
|
0x01: "V3_SWAP_EXACT_OUT",
|
||||||
|
0x02: "PERMIT2_TRANSFER_FROM",
|
||||||
|
0x03: "PERMIT2_PERMIT_BATCH",
|
||||||
|
0x04: "SWEEP",
|
||||||
|
0x05: "TRANSFER",
|
||||||
|
0x06: "PAY_PORTION",
|
||||||
|
0x08: "V2_SWAP_EXACT_IN",
|
||||||
|
0x09: "V2_SWAP_EXACT_OUT",
|
||||||
|
0x0a: "PERMIT2_PERMIT",
|
||||||
|
0x0b: "WRAP_ETH",
|
||||||
|
0x0c: "UNWRAP_WETH",
|
||||||
|
0x0d: "PERMIT2_TRANSFER_FROM_BATCH",
|
||||||
|
0x0e: "BALANCE_CHECK_ERC20",
|
||||||
|
0x10: "SEAPORT_V1_5",
|
||||||
|
0x11: "LOOKS_RARE_V2",
|
||||||
|
0x12: "NFTX",
|
||||||
|
0x13: "CRYPTOPUNKS",
|
||||||
|
0x15: "OWNER_CHECK_721",
|
||||||
|
0x16: "OWNER_CHECK_1155",
|
||||||
|
0x17: "SWEEP_ERC721",
|
||||||
|
0x18: "X2Y2_721",
|
||||||
|
0x19: "SUDOSWAP",
|
||||||
|
0x1a: "NFT20",
|
||||||
|
0x1b: "X2Y2_1155",
|
||||||
|
0x1c: "FOUNDATION",
|
||||||
|
0x1d: "SWEEP_ERC1155",
|
||||||
|
0x1e: "ELEMENT_MARKET",
|
||||||
|
0x20: "SEAPORT_V1_4",
|
||||||
|
0x21: "EXECUTE_SUB_PLAN",
|
||||||
|
0x22: "APPROVE_ERC20",
|
||||||
|
},
|
||||||
// BALANCE_CHECK_ERC20 (0x0e), its NFT market calls (0x10 to 0x13, 0x18 to
|
// BALANCE_CHECK_ERC20 (0x0e), its NFT market calls (0x10 to 0x13, 0x18 to
|
||||||
// 0x1c, 0x1e, 0x20), its NFT owner checks (0x15, 0x16) and
|
// 0x1c, 0x1e, 0x20), its NFT owner checks (0x15, 0x16) and
|
||||||
// EXECUTE_SUB_PLAN (0x21).
|
// EXECUTE_SUB_PLAN (0x21).
|
||||||
@@ -66,6 +133,28 @@ const ROUTER_V1_2 = {
|
|||||||
// UniversalRouterV2: https://github.com/Uniswap/universal-router/tree/3663f6db6e2fe121753cd2d899699c2dc75dca86/contracts
|
// UniversalRouterV2: https://github.com/Uniswap/universal-router/tree/3663f6db6e2fe121753cd2d899699c2dc75dca86/contracts
|
||||||
const ROUTER_V2 = {
|
const ROUTER_V2 = {
|
||||||
commandTypeMask: 0x3f,
|
commandTypeMask: 0x3f,
|
||||||
|
commands: {
|
||||||
|
0x00: "V3_SWAP_EXACT_IN",
|
||||||
|
0x01: "V3_SWAP_EXACT_OUT",
|
||||||
|
0x02: "PERMIT2_TRANSFER_FROM",
|
||||||
|
0x03: "PERMIT2_PERMIT_BATCH",
|
||||||
|
0x04: "SWEEP",
|
||||||
|
0x05: "TRANSFER",
|
||||||
|
0x06: "PAY_PORTION",
|
||||||
|
0x08: "V2_SWAP_EXACT_IN",
|
||||||
|
0x09: "V2_SWAP_EXACT_OUT",
|
||||||
|
0x0a: "PERMIT2_PERMIT",
|
||||||
|
0x0b: "WRAP_ETH",
|
||||||
|
0x0c: "UNWRAP_WETH",
|
||||||
|
0x0d: "PERMIT2_TRANSFER_FROM_BATCH",
|
||||||
|
0x0e: "BALANCE_CHECK_ERC20",
|
||||||
|
0x10: "V4_SWAP",
|
||||||
|
0x11: "V3_POSITION_MANAGER_PERMIT",
|
||||||
|
0x12: "V3_POSITION_MANAGER_CALL",
|
||||||
|
0x13: "V4_INITIALIZE_POOL",
|
||||||
|
0x14: "V4_POSITION_MANAGER_CALL",
|
||||||
|
0x21: "EXECUTE_SUB_PLAN",
|
||||||
|
},
|
||||||
// PERMIT2_PERMIT_BATCH (0x03), PERMIT2_PERMIT (0x0a), BALANCE_CHECK_ERC20
|
// PERMIT2_PERMIT_BATCH (0x03), PERMIT2_PERMIT (0x0a), BALANCE_CHECK_ERC20
|
||||||
// (0x0e), V3_POSITION_MANAGER_PERMIT, V3_POSITION_MANAGER_CALL,
|
// (0x0e), V3_POSITION_MANAGER_PERMIT, V3_POSITION_MANAGER_CALL,
|
||||||
// V4_INITIALIZE_POOL and V4_POSITION_MANAGER_CALL (0x11 to 0x14), and
|
// V4_INITIALIZE_POOL and V4_POSITION_MANAGER_CALL (0x11 to 0x14), and
|
||||||
@@ -77,6 +166,12 @@ const ROUTER_V2 = {
|
|||||||
// The three files are the same in both releases.
|
// The three files are the same in both releases.
|
||||||
const ROUTER_V2_1 = {
|
const ROUTER_V2_1 = {
|
||||||
commandTypeMask: 0x7f,
|
commandTypeMask: 0x7f,
|
||||||
|
// UniversalRouterV2's, and two more.
|
||||||
|
commands: {
|
||||||
|
...ROUTER_V2.commands,
|
||||||
|
0x07: "PAY_PORTION_FULL_PRECISION",
|
||||||
|
0x40: "ACROSS_V4_DEPOSIT_V3",
|
||||||
|
},
|
||||||
// The same as UniversalRouterV2's.
|
// The same as UniversalRouterV2's.
|
||||||
allowRevert: ROUTER_V2.allowRevert,
|
allowRevert: ROUTER_V2.allowRevert,
|
||||||
};
|
};
|
||||||
@@ -171,34 +266,52 @@ function v2PairFor(chainId, tokenA, tokenB) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Universal Router command types, each read from its command byte with the
|
// The Steps line's name for each command in the releases' `commands` (see
|
||||||
// router's mask (see ROUTER_V1). Only the routers with the 0x7f mask
|
// ROUTER_V1).
|
||||||
// have 0x07 and 0x40: 0x40 is above the other masks, and the other routers
|
|
||||||
// revert on 0x07, so decode() names it there as it names a command not
|
|
||||||
// listed here.
|
|
||||||
const COMMAND_NAMES = {
|
const COMMAND_NAMES = {
|
||||||
0x00: "V3 Swap (Exact In)",
|
V3_SWAP_EXACT_IN: "V3 Swap (Exact In)",
|
||||||
0x01: "V3 Swap (Exact Out)",
|
V3_SWAP_EXACT_OUT: "V3 Swap (Exact Out)",
|
||||||
0x02: "Permit2 Transfer",
|
PERMIT2_TRANSFER_FROM: "Permit2 Transfer",
|
||||||
0x03: "Permit2 Permit Batch",
|
PERMIT2_PERMIT_BATCH: "Permit2 Permit Batch",
|
||||||
0x04: "Sweep",
|
SWEEP: "Sweep",
|
||||||
0x05: "Transfer",
|
TRANSFER: "Transfer",
|
||||||
0x06: "Pay Portion",
|
PAY_PORTION: "Pay Portion",
|
||||||
0x07: "Pay Portion (Full Precision)",
|
PAY_PORTION_FULL_PRECISION: "Pay Portion (Full Precision)",
|
||||||
0x08: "V2 Swap (Exact In)",
|
V2_SWAP_EXACT_IN: "V2 Swap (Exact In)",
|
||||||
0x09: "V2 Swap (Exact Out)",
|
V2_SWAP_EXACT_OUT: "V2 Swap (Exact Out)",
|
||||||
0x0a: "Permit2 Permit",
|
PERMIT2_PERMIT: "Permit2 Permit",
|
||||||
0x0b: "Wrap ETH",
|
WRAP_ETH: "Wrap ETH",
|
||||||
0x0c: "Unwrap WETH",
|
UNWRAP_WETH: "Unwrap WETH",
|
||||||
0x0d: "Permit2 Transfer Batch",
|
PERMIT2_TRANSFER_FROM_BATCH: "Permit2 Transfer Batch",
|
||||||
0x0e: "Balance Check",
|
BALANCE_CHECK_ERC20: "Balance Check",
|
||||||
0x10: "V4 Swap",
|
V4_SWAP: "V4 Swap",
|
||||||
0x11: "V3 Position Mgr Permit",
|
V3_POSITION_MANAGER_PERMIT: "V3 Position Mgr Permit",
|
||||||
0x12: "V3 Position Mgr Call",
|
V3_POSITION_MANAGER_CALL: "V3 Position Mgr Call",
|
||||||
0x13: "V4 Initialize Pool",
|
V4_INITIALIZE_POOL: "V4 Initialize Pool",
|
||||||
0x14: "V4 Position Mgr Call",
|
V4_POSITION_MANAGER_CALL: "V4 Position Mgr Call",
|
||||||
0x21: "Execute Sub-Plan",
|
EXECUTE_SUB_PLAN: "Execute Sub-Plan",
|
||||||
0x40: "Across Bridge Deposit",
|
ACROSS_V4_DEPOSIT_V3: "Across Bridge Deposit",
|
||||||
|
// The NFT commands of UniversalRouterV1 and UniversalRouterV1_2_V2Support,
|
||||||
|
// and the latter's APPROVE_ERC20.
|
||||||
|
SEAPORT: "Seaport",
|
||||||
|
SEAPORT_V1_4: "Seaport V1.4",
|
||||||
|
SEAPORT_V1_5: "Seaport V1.5",
|
||||||
|
LOOKS_RARE_721: "LooksRare 721",
|
||||||
|
LOOKS_RARE_1155: "LooksRare 1155",
|
||||||
|
LOOKS_RARE_V2: "LooksRare V2",
|
||||||
|
NFTX: "NFTX",
|
||||||
|
CRYPTOPUNKS: "CryptoPunks",
|
||||||
|
OWNER_CHECK_721: "Owner Check 721",
|
||||||
|
OWNER_CHECK_1155: "Owner Check 1155",
|
||||||
|
SWEEP_ERC721: "Sweep ERC-721",
|
||||||
|
SWEEP_ERC1155: "Sweep ERC-1155",
|
||||||
|
X2Y2_721: "X2Y2 721",
|
||||||
|
X2Y2_1155: "X2Y2 1155",
|
||||||
|
SUDOSWAP: "Sudoswap",
|
||||||
|
NFT20: "NFT20",
|
||||||
|
FOUNDATION: "Foundation",
|
||||||
|
ELEMENT_MARKET: "Element Market",
|
||||||
|
APPROVE_ERC20: "Approve ERC-20",
|
||||||
};
|
};
|
||||||
|
|
||||||
// The swap's Amount and Min. received lines land on the same approval screen,
|
// The swap's Amount and Min. received lines land on the same approval screen,
|
||||||
@@ -913,7 +1026,7 @@ function decodeV4Swap(input) {
|
|||||||
function decode(data, toAddress, chainId, sources, fromAddress) {
|
function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||||
const release = routerRelease(toAddress, chainId);
|
const release = routerRelease(toAddress, chainId);
|
||||||
if (release === null) return null;
|
if (release === null) return null;
|
||||||
const { commandTypeMask, allowRevert } = release;
|
const { commandTypeMask, commands, allowRevert } = release;
|
||||||
try {
|
try {
|
||||||
const parsed = ROUTER_IFACE.parseTransaction({ data });
|
const parsed = ROUTER_IFACE.parseTransaction({ data });
|
||||||
if (!parsed) return null;
|
if (!parsed) return null;
|
||||||
@@ -1026,7 +1139,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// router adds only what it holds.
|
// router adds only what it holds.
|
||||||
const isHop = (i, token, recipient) => {
|
const isHop = (i, token, recipient) => {
|
||||||
if (i + 1 >= commandsBytes.length) return false;
|
if (i + 1 >= commandsBytes.length) return false;
|
||||||
if ((commandsBytes[i + 1] & commandTypeMask) !== 0x08) return false;
|
const nextCommand =
|
||||||
|
commands[commandsBytes[i + 1] & commandTypeMask];
|
||||||
|
if (nextCommand !== "V2_SWAP_EXACT_IN") return false;
|
||||||
const next = decodeV2SwapExactIn(inputs[i + 1]);
|
const next = decodeV2SwapExactIn(inputs[i + 1]);
|
||||||
return (
|
return (
|
||||||
next !== null &&
|
next !== null &&
|
||||||
@@ -1042,23 +1157,22 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
|
|
||||||
for (let i = 0; i < commandsBytes.length; i++) {
|
for (let i = 0; i < commandsBytes.length; i++) {
|
||||||
const cmdId = commandsBytes[i] & commandTypeMask;
|
const cmdId = commandsBytes[i] & commandTypeMask;
|
||||||
|
// The command this release runs for the step (see ROUTER_V1),
|
||||||
|
// undefined for a type it names no command for.
|
||||||
|
const command = commands[cmdId];
|
||||||
// The router goes on to the next step if this one fails (see
|
// The router goes on to the next step if this one fails (see
|
||||||
// ROUTER_V1). The flag on any other command has no effect.
|
// ROUTER_V1). The flag on any other command has no effect.
|
||||||
const allowedToFail =
|
const allowedToFail =
|
||||||
(commandsBytes[i] & ALLOW_REVERT_FLAG) !== 0 &&
|
(commandsBytes[i] & ALLOW_REVERT_FLAG) !== 0 &&
|
||||||
allowRevert.includes(cmdId);
|
allowRevert.includes(cmdId);
|
||||||
// Only the routers with the 0x7f mask have command 0x07 (see
|
|
||||||
// COMMAND_NAMES).
|
|
||||||
const name =
|
|
||||||
cmdId === 0x07 && commandTypeMask !== 0x7f
|
|
||||||
? null
|
|
||||||
: COMMAND_NAMES[cmdId];
|
|
||||||
commandNames.push(
|
commandNames.push(
|
||||||
name || "Command 0x" + cmdId.toString(16).padStart(2, "0"),
|
present(command)
|
||||||
|
? COMMAND_NAMES[command]
|
||||||
|
: "Command 0x" + cmdId.toString(16).padStart(2, "0"),
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (cmdId === 0x21) {
|
if (command === "EXECUTE_SUB_PLAN") {
|
||||||
// EXECUTE_SUB_PLAN runs a further list of commands held in
|
// EXECUTE_SUB_PLAN runs a further list of commands held in
|
||||||
// its input, which this decoder does not read. The step
|
// its input, which this decoder does not read. The step
|
||||||
// says so, and after this loop neither side is named.
|
// says so, and after this loop neither side is named.
|
||||||
@@ -1066,13 +1180,13 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
hasSubPlan = true;
|
hasSubPlan = true;
|
||||||
commandNames[i] += " (its steps are not shown)";
|
commandNames[i] += " (its steps are not shown)";
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
} else if (cmdId === 0x0a) {
|
} else if (command === "PERMIT2_PERMIT") {
|
||||||
const p = decodePermit2(inputs[i]);
|
const p = decodePermit2(inputs[i]);
|
||||||
// A permit states both halves itself, so it may replace an
|
// A permit states both halves itself, so it may replace an
|
||||||
// input side an earlier hop established without breaking
|
// input side an earlier hop established without breaking
|
||||||
// the invariant.
|
// the invariant.
|
||||||
if (p) setInput(p.token, p.amount);
|
if (p) setInput(p.token, p.amount);
|
||||||
} else if (cmdId === 0x02) {
|
} else if (command === "PERMIT2_TRANSFER_FROM") {
|
||||||
const p = decodePermit2TransferFrom(inputs[i]);
|
const p = decodePermit2TransferFrom(inputs[i]);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
@@ -1091,7 +1205,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else if (!isHop(i, p.token, p.recipient)) {
|
} else if (!isHop(i, p.token, p.recipient)) {
|
||||||
addPayee(p.recipient);
|
addPayee(p.recipient);
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x0e) {
|
} else if (command === "BALANCE_CHECK_ERC20") {
|
||||||
const b = decodeBalanceCheck(inputs[i]);
|
const b = decodeBalanceCheck(inputs[i]);
|
||||||
if (!b) {
|
if (!b) {
|
||||||
// The router still runs a check whose input does not
|
// The router still runs a check whose input does not
|
||||||
@@ -1121,7 +1235,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
setOutput(b.token, minBalance);
|
setOutput(b.token, minBalance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x00) {
|
} else if (command === "V3_SWAP_EXACT_IN") {
|
||||||
const s = decodeV3SwapExactIn(inputs[i]);
|
const s = decodeV3SwapExactIn(inputs[i]);
|
||||||
if (s) {
|
if (s) {
|
||||||
setInputOnce(s.tokenIn, s.amountIn);
|
setInputOnce(s.tokenIn, s.amountIn);
|
||||||
@@ -1135,7 +1249,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x08) {
|
} else if (command === "V2_SWAP_EXACT_IN") {
|
||||||
const s = decodeV2SwapExactIn(inputs[i]);
|
const s = decodeV2SwapExactIn(inputs[i]);
|
||||||
if (s) {
|
if (s) {
|
||||||
setInputOnce(s.tokenIn, s.amountIn);
|
setInputOnce(s.tokenIn, s.amountIn);
|
||||||
@@ -1146,7 +1260,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x09) {
|
} else if (command === "V2_SWAP_EXACT_OUT") {
|
||||||
// Buys exactly amountOut and spends at most amountInMax.
|
// Buys exactly amountOut and spends at most amountInMax.
|
||||||
hasV2ExactOut = true;
|
hasV2ExactOut = true;
|
||||||
const s = decodeV2SwapExactOut(inputs[i]);
|
const s = decodeV2SwapExactOut(inputs[i]);
|
||||||
@@ -1159,7 +1273,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x04) {
|
} else if (command === "SWEEP") {
|
||||||
const s = decodeSweep(inputs[i]);
|
const s = decodeSweep(inputs[i]);
|
||||||
if (s) {
|
if (s) {
|
||||||
addPayee(s.recipient);
|
addPayee(s.recipient);
|
||||||
@@ -1170,9 +1284,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
cmdId === 0x05 ||
|
command === "TRANSFER" ||
|
||||||
cmdId === 0x06 ||
|
command === "PAY_PORTION" ||
|
||||||
(cmdId === 0x07 && commandTypeMask === 0x7f)
|
command === "PAY_PORTION_FULL_PRECISION"
|
||||||
) {
|
) {
|
||||||
const t = decodeTransfer(inputs[i]);
|
const t = decodeTransfer(inputs[i]);
|
||||||
if (t) {
|
if (t) {
|
||||||
@@ -1183,16 +1297,16 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// PAY_PORTION_FULL_PRECISION pays all the router
|
// PAY_PORTION_FULL_PRECISION pays all the router
|
||||||
// holds of the token, as SWEEP does.
|
// holds of the token, as SWEEP does.
|
||||||
const all =
|
const all =
|
||||||
cmdId === 0x05
|
command === "TRANSFER"
|
||||||
? CONTRACT_BALANCE
|
? CONTRACT_BALANCE
|
||||||
: cmdId === 0x06
|
: command === "PAY_PORTION"
|
||||||
? 10000n
|
? 10000n
|
||||||
: 10n ** 18n;
|
: 10n ** 18n;
|
||||||
// A TRANSFER of ETH has no such amount: the router
|
// A TRANSFER of ETH has no such amount: the router
|
||||||
// sends ETH as written, so a TRANSFER of
|
// sends ETH as written, so a TRANSFER of
|
||||||
// CONTRACT_BALANCE of ETH always reverts.
|
// CONTRACT_BALANCE of ETH always reverts.
|
||||||
const ethTransfer =
|
const ethTransfer =
|
||||||
cmdId === 0x05 &&
|
command === "TRANSFER" &&
|
||||||
t.token ===
|
t.token ===
|
||||||
"0x0000000000000000000000000000000000000000";
|
"0x0000000000000000000000000000000000000000";
|
||||||
if (
|
if (
|
||||||
@@ -1205,9 +1319,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x40) {
|
} else if (command === "ACROSS_V4_DEPOSIT_V3") {
|
||||||
// Only the routers with the 0x7f mask read a command type
|
|
||||||
// of 0x40.
|
|
||||||
const a = decodeAcrossV4DepositV3(inputs[i]);
|
const a = decodeAcrossV4DepositV3(inputs[i]);
|
||||||
if (a) {
|
if (a) {
|
||||||
bridgeDeposit = true;
|
bridgeDeposit = true;
|
||||||
@@ -1230,7 +1342,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x0b) {
|
} else if (command === "WRAP_ETH") {
|
||||||
const w = decodeWrapEth(inputs[i]);
|
const w = decodeWrapEth(inputs[i]);
|
||||||
if (w) {
|
if (w) {
|
||||||
setInputOnce(
|
setInputOnce(
|
||||||
@@ -1241,17 +1353,8 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x10) {
|
} else if (command === "V4_SWAP") {
|
||||||
let v4 = decodeV4Swap(inputs[i]);
|
const v4 = decodeV4Swap(inputs[i]);
|
||||||
// A step the router lets fail guarantees nothing, so a
|
|
||||||
// minimum it states is read as zero, as a
|
|
||||||
// BALANCE_CHECK_ERC20's is. Only UniversalRouterV1 and
|
|
||||||
// UniversalRouterV1_2_V2Support let this command fail; on
|
|
||||||
// them it is an NFT market call, still read here as a V4
|
|
||||||
// swap (https://git.eeqj.de/sneak/AutistMask/issues/592).
|
|
||||||
if (v4 && allowedToFail && present(v4.amountOutMin)) {
|
|
||||||
v4 = { ...v4, amountOutMin: 0n };
|
|
||||||
}
|
|
||||||
if (v4) {
|
if (v4) {
|
||||||
for (const t of v4.takes) {
|
for (const t of v4.takes) {
|
||||||
if (!isHop(i, t.currency, t.recipient)) {
|
if (!isHop(i, t.currency, t.recipient)) {
|
||||||
@@ -1292,7 +1395,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
setInputOnce(v4.tokenIn, v4.amountIn);
|
setInputOnce(v4.tokenIn, v4.amountIn);
|
||||||
setOutput(v4.tokenOut, v4.amountOutMin);
|
setOutput(v4.tokenOut, v4.amountOutMin);
|
||||||
}
|
}
|
||||||
} else if (cmdId === 0x0c) {
|
} else if (command === "UNWRAP_WETH") {
|
||||||
hasUnwrapWeth = true;
|
hasUnwrapWeth = true;
|
||||||
const u = decodeUnwrapWeth(inputs[i]);
|
const u = decodeUnwrapWeth(inputs[i]);
|
||||||
if (u) {
|
if (u) {
|
||||||
@@ -1306,9 +1409,10 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Any other command may pay an address this decoder does
|
// Any other step may pay an address this decoder does not
|
||||||
// not read: V3_SWAP_EXACT_OUT and
|
// read: V3_SWAP_EXACT_OUT, PERMIT2_TRANSFER_FROM_BATCH and
|
||||||
// PERMIT2_TRANSFER_FROM_BATCH among them.
|
// the NFT commands among them, and a type the release
|
||||||
|
// names no command for.
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
+304
-29
@@ -2,7 +2,9 @@
|
|||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/594 and
|
// (https://git.eeqj.de/sneak/AutistMask/issues/594 and
|
||||||
// https://git.eeqj.de/sneak/AutistMask/issues/599), the allow-revert flag
|
// https://git.eeqj.de/sneak/AutistMask/issues/599), the allow-revert flag
|
||||||
// on the steps that pay it
|
// on the steps that pay it
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/602), and the address whose
|
// (https://git.eeqj.de/sneak/AutistMask/issues/602), the command each router
|
||||||
|
// release runs for a command type
|
||||||
|
// (https://git.eeqj.de/sneak/AutistMask/issues/592), and the address whose
|
||||||
// balance a BALANCE_CHECK_ERC20 checks
|
// balance a BALANCE_CHECK_ERC20 checks
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/600).
|
// (https://git.eeqj.de/sneak/AutistMask/issues/600).
|
||||||
//
|
//
|
||||||
@@ -1570,32 +1572,6 @@ describe("the allow-revert flag", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// On these two routers 0x10 is an NFT market call the router lets fail,
|
|
||||||
// still read as a V4 swap
|
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/592).
|
|
||||||
test.each([
|
|
||||||
{ router: "UniversalRouterV1", address: ROUTER_V1 },
|
|
||||||
{ router: "UniversalRouterV1_2_V2Support", address: ROUTER_V1_2 },
|
|
||||||
])(
|
|
||||||
"on $router, a V4_SWAP with the flag is allowed to fail and states no minimum",
|
|
||||||
({ address }) => {
|
|
||||||
const step = v4Swap(SWAP_USDT_FOR_USDC, SETTLE_USDT, takeAll(USDC));
|
|
||||||
|
|
||||||
const plain = decode(execute(step), USER, address);
|
|
||||||
expect(detail(plain, "Steps").value).toBe("V4 Swap");
|
|
||||||
expect(detail(plain, "Min. received").value).toBe("1990.0000 USDC");
|
|
||||||
|
|
||||||
const withFlag = decode(execute(flagged(step)), USER, address);
|
|
||||||
expect(detail(withFlag, "Steps").value).toBe(
|
|
||||||
"V4 Swap (allowed to fail)",
|
|
||||||
);
|
|
||||||
expect(detail(withFlag, "Token Out").address).toBe(USDC);
|
|
||||||
expect(detail(withFlag, "Min. received").value).toBe(
|
|
||||||
"None (no minimum guaranteed)",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Only the routers from UniversalRouterV2 on let a PERMIT2_PERMIT fail.
|
// Only the routers from UniversalRouterV2 on let a PERMIT2_PERMIT fail.
|
||||||
test.each([
|
test.each([
|
||||||
{ router: "UniversalRouterV1", address: ROUTER_V1, mark: "" },
|
{ router: "UniversalRouterV1", address: ROUTER_V1, mark: "" },
|
||||||
@@ -1663,14 +1639,14 @@ describe("the allow-revert flag", () => {
|
|||||||
address: ROUTER_V1,
|
address: ROUTER_V1,
|
||||||
command: "FOUNDATION",
|
command: "FOUNDATION",
|
||||||
id: 0x1c,
|
id: 0x1c,
|
||||||
name: "Command 0x1c",
|
name: "Foundation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
router: "UniversalRouterV1_2_V2Support",
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
address: ROUTER_V1_2,
|
address: ROUTER_V1_2,
|
||||||
command: "ELEMENT_MARKET",
|
command: "ELEMENT_MARKET",
|
||||||
id: 0x1e,
|
id: 0x1e,
|
||||||
name: "Command 0x1e",
|
name: "Element Market",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
router: "UniversalRouterV2",
|
router: "UniversalRouterV2",
|
||||||
@@ -1718,6 +1694,305 @@ describe("the allow-revert flag", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://git.eeqj.de/sneak/AutistMask/issues/592: each release runs its own
|
||||||
|
// command for a command type, as its Commands.sol names it. The decoder named
|
||||||
|
// and read every type as UniversalRouterV2 does, so on the two older routers
|
||||||
|
// an NFT command read as a V4 swap, and on UniversalRouterV1, which reverts on
|
||||||
|
// 0x0e, that type read as a balance check that set the minimum.
|
||||||
|
describe("the command each release runs for a command type", () => {
|
||||||
|
// Inputs UniversalRouterV2 reads as a V4 swap of 2,000 USDT for at least
|
||||||
|
// 1,990 USDC paid to the user, and as a check that the user holds at
|
||||||
|
// least 1 WETH.
|
||||||
|
const swapInput = v4Swap(SWAP_USDT_FOR_USDC, SETTLE_USDT, takeAll(USDC))[1];
|
||||||
|
const checkInput = balanceCheck(WETH, ONE_WETH)[1];
|
||||||
|
// The lines of a transaction whose one step the decoder does not read.
|
||||||
|
const NOT_READ = [
|
||||||
|
"Protocol",
|
||||||
|
"Token In",
|
||||||
|
"Token Out",
|
||||||
|
"Paid to",
|
||||||
|
"Steps",
|
||||||
|
"Deadline",
|
||||||
|
];
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x0e,
|
||||||
|
input: checkInput,
|
||||||
|
name: "Command 0x0e",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x10,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Seaport",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x11,
|
||||||
|
input: swapInput,
|
||||||
|
name: "LooksRare 721",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x12,
|
||||||
|
input: swapInput,
|
||||||
|
name: "NFTX",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x13,
|
||||||
|
input: swapInput,
|
||||||
|
name: "CryptoPunks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x14,
|
||||||
|
input: swapInput,
|
||||||
|
name: "LooksRare 1155",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x15,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Owner Check 721",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x16,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Owner Check 1155",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x17,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Sweep ERC-721",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x18,
|
||||||
|
input: swapInput,
|
||||||
|
name: "X2Y2 721",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x19,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Sudoswap",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x1a,
|
||||||
|
input: swapInput,
|
||||||
|
name: "NFT20",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x1b,
|
||||||
|
input: swapInput,
|
||||||
|
name: "X2Y2 1155",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x1c,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Foundation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1",
|
||||||
|
address: ROUTER_V1,
|
||||||
|
id: 0x1d,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Sweep ERC-1155",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x10,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Seaport V1.5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x11,
|
||||||
|
input: swapInput,
|
||||||
|
name: "LooksRare V2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x12,
|
||||||
|
input: swapInput,
|
||||||
|
name: "NFTX",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x13,
|
||||||
|
input: swapInput,
|
||||||
|
name: "CryptoPunks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x14,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Command 0x14",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x15,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Owner Check 721",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x16,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Owner Check 1155",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x17,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Sweep ERC-721",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x18,
|
||||||
|
input: swapInput,
|
||||||
|
name: "X2Y2 721",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x19,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Sudoswap",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x1a,
|
||||||
|
input: swapInput,
|
||||||
|
name: "NFT20",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x1b,
|
||||||
|
input: swapInput,
|
||||||
|
name: "X2Y2 1155",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x1c,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Foundation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x1d,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Sweep ERC-1155",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x1e,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Element Market",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x20,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Seaport V1.4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV1_2_V2Support",
|
||||||
|
address: ROUTER_V1_2,
|
||||||
|
id: 0x22,
|
||||||
|
input: swapInput,
|
||||||
|
name: "Approve ERC-20",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV2",
|
||||||
|
address: ROUTER,
|
||||||
|
id: 0x11,
|
||||||
|
input: swapInput,
|
||||||
|
name: "V3 Position Mgr Permit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV2",
|
||||||
|
address: ROUTER,
|
||||||
|
id: 0x12,
|
||||||
|
input: swapInput,
|
||||||
|
name: "V3 Position Mgr Call",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
router: "UniversalRouterV2",
|
||||||
|
address: ROUTER,
|
||||||
|
id: 0x13,
|
||||||
|
input: swapInput,
|
||||||
|
name: "V4 Initialize Pool",
|
||||||
|
},
|
||||||
|
])(
|
||||||
|
"on $router, '$name' is a step the decoder does not read",
|
||||||
|
({ address, id, input, name }) => {
|
||||||
|
const result = decode(execute([id, input]), USER, address);
|
||||||
|
expect(detail(result, "Steps").value).toBe(name);
|
||||||
|
expect(result.name).toBe("Uniswap Swap");
|
||||||
|
expect(labels(result)).toEqual(NOT_READ);
|
||||||
|
expect(paidTo(result)).toEqual([UNDECODED]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
{ router: "UniversalRouterV1_2_V2Support", address: ROUTER_V1_2 },
|
||||||
|
{ router: "UniversalRouterV2", address: ROUTER },
|
||||||
|
])(
|
||||||
|
"on $router, 0x0e is a balance check, which sets the minimum",
|
||||||
|
({ address }) => {
|
||||||
|
const result = decode(execute([0x0e, checkInput]), USER, address);
|
||||||
|
expect(detail(result, "Steps").value).toBe("Balance Check");
|
||||||
|
expect(detail(result, "Token Out").address).toBe(WETH);
|
||||||
|
expect(detail(result, "Min. paid out").value).toBe("1.0000 WETH");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test("on UniversalRouterV2, 0x10 is a V4 swap", () => {
|
||||||
|
const result = decode(execute([0x10, swapInput]), USER, ROUTER);
|
||||||
|
expect(detail(result, "Steps").value).toBe("V4 Swap");
|
||||||
|
expect(result.name).toBe("Swap USDT → USDC");
|
||||||
|
expect(detail(result, "Min. received").value).toBe("1990.0000 USDC");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// https://git.eeqj.de/sneak/AutistMask/issues/600: on every release that has
|
// https://git.eeqj.de/sneak/AutistMask/issues/600: on every release that has
|
||||||
// BALANCE_CHECK_ERC20, the router checks the balance of the owner as written,
|
// BALANCE_CHECK_ERC20, the router checks the balance of the owner as written,
|
||||||
// without map(), so address(1) and address(2) there are those addresses, not
|
// without map(), so address(1) and address(2) there are those addresses, not
|
||||||
|
|||||||
Reference in New Issue
Block a user