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:
+175
-71
@@ -24,8 +24,8 @@ const ROUTER_IFACE = new Interface([
|
||||
"function execute(bytes commands, bytes[] inputs, uint256 deadline)",
|
||||
]);
|
||||
|
||||
// The Universal Router releases deployed at the addresses below, each with two
|
||||
// things read from its source at the commit linked for it:
|
||||
// The Universal Router releases deployed at the addresses below, each with
|
||||
// three things read from its source at the commit linked for it:
|
||||
//
|
||||
// - `commandTypeMask`, the mask it reads a command's type with: its dispatcher
|
||||
// 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
|
||||
// `contracts/libraries/Commands.sol`, the same file as in the source
|
||||
// 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
|
||||
// transaction. Its `execute()`, in `contracts/UniversalRouter.sol`, runs each
|
||||
// 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
|
||||
const ROUTER_V1 = {
|
||||
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
|
||||
// (0x15, 0x16).
|
||||
allowRevert: [
|
||||
@@ -55,6 +89,39 @@ const ROUTER_V1 = {
|
||||
// UniversalRouterV1_2_V2Support: https://github.com/Uniswap/universal-router/tree/41183d6eb154f0ab0e74a0e911a5ef9ea51fc4bd/contracts
|
||||
const ROUTER_V1_2 = {
|
||||
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
|
||||
// 0x1c, 0x1e, 0x20), its NFT owner checks (0x15, 0x16) and
|
||||
// EXECUTE_SUB_PLAN (0x21).
|
||||
@@ -66,6 +133,28 @@ const ROUTER_V1_2 = {
|
||||
// UniversalRouterV2: https://github.com/Uniswap/universal-router/tree/3663f6db6e2fe121753cd2d899699c2dc75dca86/contracts
|
||||
const ROUTER_V2 = {
|
||||
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
|
||||
// (0x0e), V3_POSITION_MANAGER_PERMIT, V3_POSITION_MANAGER_CALL,
|
||||
// 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.
|
||||
const ROUTER_V2_1 = {
|
||||
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.
|
||||
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
|
||||
// router's mask (see ROUTER_V1). Only the routers with the 0x7f mask
|
||||
// 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.
|
||||
// The Steps line's name for each command in the releases' `commands` (see
|
||||
// ROUTER_V1).
|
||||
const COMMAND_NAMES = {
|
||||
0x00: "V3 Swap (Exact In)",
|
||||
0x01: "V3 Swap (Exact Out)",
|
||||
0x02: "Permit2 Transfer",
|
||||
0x03: "Permit2 Permit Batch",
|
||||
0x04: "Sweep",
|
||||
0x05: "Transfer",
|
||||
0x06: "Pay Portion",
|
||||
0x07: "Pay Portion (Full Precision)",
|
||||
0x08: "V2 Swap (Exact In)",
|
||||
0x09: "V2 Swap (Exact Out)",
|
||||
0x0a: "Permit2 Permit",
|
||||
0x0b: "Wrap ETH",
|
||||
0x0c: "Unwrap WETH",
|
||||
0x0d: "Permit2 Transfer Batch",
|
||||
0x0e: "Balance Check",
|
||||
0x10: "V4 Swap",
|
||||
0x11: "V3 Position Mgr Permit",
|
||||
0x12: "V3 Position Mgr Call",
|
||||
0x13: "V4 Initialize Pool",
|
||||
0x14: "V4 Position Mgr Call",
|
||||
0x21: "Execute Sub-Plan",
|
||||
0x40: "Across Bridge Deposit",
|
||||
V3_SWAP_EXACT_IN: "V3 Swap (Exact In)",
|
||||
V3_SWAP_EXACT_OUT: "V3 Swap (Exact Out)",
|
||||
PERMIT2_TRANSFER_FROM: "Permit2 Transfer",
|
||||
PERMIT2_PERMIT_BATCH: "Permit2 Permit Batch",
|
||||
SWEEP: "Sweep",
|
||||
TRANSFER: "Transfer",
|
||||
PAY_PORTION: "Pay Portion",
|
||||
PAY_PORTION_FULL_PRECISION: "Pay Portion (Full Precision)",
|
||||
V2_SWAP_EXACT_IN: "V2 Swap (Exact In)",
|
||||
V2_SWAP_EXACT_OUT: "V2 Swap (Exact Out)",
|
||||
PERMIT2_PERMIT: "Permit2 Permit",
|
||||
WRAP_ETH: "Wrap ETH",
|
||||
UNWRAP_WETH: "Unwrap WETH",
|
||||
PERMIT2_TRANSFER_FROM_BATCH: "Permit2 Transfer Batch",
|
||||
BALANCE_CHECK_ERC20: "Balance Check",
|
||||
V4_SWAP: "V4 Swap",
|
||||
V3_POSITION_MANAGER_PERMIT: "V3 Position Mgr Permit",
|
||||
V3_POSITION_MANAGER_CALL: "V3 Position Mgr Call",
|
||||
V4_INITIALIZE_POOL: "V4 Initialize Pool",
|
||||
V4_POSITION_MANAGER_CALL: "V4 Position Mgr Call",
|
||||
EXECUTE_SUB_PLAN: "Execute Sub-Plan",
|
||||
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,
|
||||
@@ -913,7 +1026,7 @@ function decodeV4Swap(input) {
|
||||
function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
const release = routerRelease(toAddress, chainId);
|
||||
if (release === null) return null;
|
||||
const { commandTypeMask, allowRevert } = release;
|
||||
const { commandTypeMask, commands, allowRevert } = release;
|
||||
try {
|
||||
const parsed = ROUTER_IFACE.parseTransaction({ data });
|
||||
if (!parsed) return null;
|
||||
@@ -1026,7 +1139,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
// router adds only what it holds.
|
||||
const isHop = (i, token, recipient) => {
|
||||
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]);
|
||||
return (
|
||||
next !== null &&
|
||||
@@ -1042,23 +1157,22 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
|
||||
for (let i = 0; i < commandsBytes.length; i++) {
|
||||
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
|
||||
// ROUTER_V1). The flag on any other command has no effect.
|
||||
const allowedToFail =
|
||||
(commandsBytes[i] & ALLOW_REVERT_FLAG) !== 0 &&
|
||||
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(
|
||||
name || "Command 0x" + cmdId.toString(16).padStart(2, "0"),
|
||||
present(command)
|
||||
? COMMAND_NAMES[command]
|
||||
: "Command 0x" + cmdId.toString(16).padStart(2, "0"),
|
||||
);
|
||||
|
||||
try {
|
||||
if (cmdId === 0x21) {
|
||||
if (command === "EXECUTE_SUB_PLAN") {
|
||||
// EXECUTE_SUB_PLAN runs a further list of commands held in
|
||||
// its input, which this decoder does not read. The step
|
||||
// says so, and after this loop neither side is named.
|
||||
@@ -1066,13 +1180,13 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
hasSubPlan = true;
|
||||
commandNames[i] += " (its steps are not shown)";
|
||||
payeeUnread = true;
|
||||
} else if (cmdId === 0x0a) {
|
||||
} else if (command === "PERMIT2_PERMIT") {
|
||||
const p = decodePermit2(inputs[i]);
|
||||
// A permit states both halves itself, so it may replace an
|
||||
// input side an earlier hop established without breaking
|
||||
// the invariant.
|
||||
if (p) setInput(p.token, p.amount);
|
||||
} else if (cmdId === 0x02) {
|
||||
} else if (command === "PERMIT2_TRANSFER_FROM") {
|
||||
const p = decodePermit2TransferFrom(inputs[i]);
|
||||
if (!p) {
|
||||
payeeUnread = true;
|
||||
@@ -1091,7 +1205,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
} else if (!isHop(i, p.token, p.recipient)) {
|
||||
addPayee(p.recipient);
|
||||
}
|
||||
} else if (cmdId === 0x0e) {
|
||||
} else if (command === "BALANCE_CHECK_ERC20") {
|
||||
const b = decodeBalanceCheck(inputs[i]);
|
||||
if (!b) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
} else if (cmdId === 0x00) {
|
||||
} else if (command === "V3_SWAP_EXACT_IN") {
|
||||
const s = decodeV3SwapExactIn(inputs[i]);
|
||||
if (s) {
|
||||
setInputOnce(s.tokenIn, s.amountIn);
|
||||
@@ -1135,7 +1249,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
} else {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else if (cmdId === 0x08) {
|
||||
} else if (command === "V2_SWAP_EXACT_IN") {
|
||||
const s = decodeV2SwapExactIn(inputs[i]);
|
||||
if (s) {
|
||||
setInputOnce(s.tokenIn, s.amountIn);
|
||||
@@ -1146,7 +1260,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
} else {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else if (cmdId === 0x09) {
|
||||
} else if (command === "V2_SWAP_EXACT_OUT") {
|
||||
// Buys exactly amountOut and spends at most amountInMax.
|
||||
hasV2ExactOut = true;
|
||||
const s = decodeV2SwapExactOut(inputs[i]);
|
||||
@@ -1159,7 +1273,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
} else {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else if (cmdId === 0x04) {
|
||||
} else if (command === "SWEEP") {
|
||||
const s = decodeSweep(inputs[i]);
|
||||
if (s) {
|
||||
addPayee(s.recipient);
|
||||
@@ -1170,9 +1284,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else if (
|
||||
cmdId === 0x05 ||
|
||||
cmdId === 0x06 ||
|
||||
(cmdId === 0x07 && commandTypeMask === 0x7f)
|
||||
command === "TRANSFER" ||
|
||||
command === "PAY_PORTION" ||
|
||||
command === "PAY_PORTION_FULL_PRECISION"
|
||||
) {
|
||||
const t = decodeTransfer(inputs[i]);
|
||||
if (t) {
|
||||
@@ -1183,16 +1297,16 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
// PAY_PORTION_FULL_PRECISION pays all the router
|
||||
// holds of the token, as SWEEP does.
|
||||
const all =
|
||||
cmdId === 0x05
|
||||
command === "TRANSFER"
|
||||
? CONTRACT_BALANCE
|
||||
: cmdId === 0x06
|
||||
: command === "PAY_PORTION"
|
||||
? 10000n
|
||||
: 10n ** 18n;
|
||||
// A TRANSFER of ETH has no such amount: the router
|
||||
// sends ETH as written, so a TRANSFER of
|
||||
// CONTRACT_BALANCE of ETH always reverts.
|
||||
const ethTransfer =
|
||||
cmdId === 0x05 &&
|
||||
command === "TRANSFER" &&
|
||||
t.token ===
|
||||
"0x0000000000000000000000000000000000000000";
|
||||
if (
|
||||
@@ -1205,9 +1319,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
} else {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else if (cmdId === 0x40) {
|
||||
// Only the routers with the 0x7f mask read a command type
|
||||
// of 0x40.
|
||||
} else if (command === "ACROSS_V4_DEPOSIT_V3") {
|
||||
const a = decodeAcrossV4DepositV3(inputs[i]);
|
||||
if (a) {
|
||||
bridgeDeposit = true;
|
||||
@@ -1230,7 +1342,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
} else {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else if (cmdId === 0x0b) {
|
||||
} else if (command === "WRAP_ETH") {
|
||||
const w = decodeWrapEth(inputs[i]);
|
||||
if (w) {
|
||||
setInputOnce(
|
||||
@@ -1241,17 +1353,8 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
} else {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else if (cmdId === 0x10) {
|
||||
let 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 };
|
||||
}
|
||||
} else if (command === "V4_SWAP") {
|
||||
const v4 = decodeV4Swap(inputs[i]);
|
||||
if (v4) {
|
||||
for (const t of v4.takes) {
|
||||
if (!isHop(i, t.currency, t.recipient)) {
|
||||
@@ -1292,7 +1395,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
setInputOnce(v4.tokenIn, v4.amountIn);
|
||||
setOutput(v4.tokenOut, v4.amountOutMin);
|
||||
}
|
||||
} else if (cmdId === 0x0c) {
|
||||
} else if (command === "UNWRAP_WETH") {
|
||||
hasUnwrapWeth = true;
|
||||
const u = decodeUnwrapWeth(inputs[i]);
|
||||
if (u) {
|
||||
@@ -1306,9 +1409,10 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
payeeUnread = true;
|
||||
}
|
||||
} else {
|
||||
// Any other command may pay an address this decoder does
|
||||
// not read: V3_SWAP_EXACT_OUT and
|
||||
// PERMIT2_TRANSFER_FROM_BATCH among them.
|
||||
// Any other step may pay an address this decoder does not
|
||||
// read: V3_SWAP_EXACT_OUT, PERMIT2_TRANSFER_FROM_BATCH and
|
||||
// the NFT commands among them, and a type the release
|
||||
// names no command for.
|
||||
payeeUnread = true;
|
||||
}
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user