Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5f9d3c498 |
@@ -2971,15 +2971,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`
|
||||||
@@ -2992,11 +3002,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
|
||||||
@@ -3071,7 +3077,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 {
|
||||||
|
|||||||
+143
-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,144 @@ 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_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: "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