Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7739547b3 |
@@ -2934,17 +2934,20 @@ Currently supported:
|
||||
address, is not counted as paid out. When a swap step or `TAKE` pays the
|
||||
router the swap's output token, the output stays in the router unless a later
|
||||
`SWEEP` of that token, or a later `UNWRAP_WETH` when it is WETH, pays it out.
|
||||
A swap step paying an address that the very next step spends, as a V2 exact-in
|
||||
of the same token whose `amountIn` is `ALREADY_PAID` (zero) or
|
||||
`CONTRACT_BALANCE`, is a hop, not a payout: Uniswap's mixed routes pay the
|
||||
next V2 pair directly, and the router enforces that swap's own minimum. Every
|
||||
other payment counts, in whichever step and of whatever token. The minimum
|
||||
reads `Min. received` only when some step pays the transaction's sender (the
|
||||
From address on TxApproval, the sender the explorer reports on
|
||||
TransactionDetail), no step pays any other address, no step that may pay an
|
||||
address went unread, and the output does not stay in the router. Otherwise it
|
||||
reads `Min. paid out`, a `Paid to` line after `Token Out` names each other
|
||||
address paid, one reads
|
||||
A swap step or `TAKE` paying a token to the V2 pair that the very next step
|
||||
spends, as a V2 exact-in of that token whose `amountIn` is `ALREADY_PAID`
|
||||
(zero) or `CONTRACT_BALANCE`, is a hop, not a payout: Uniswap's mixed routes
|
||||
pay the next V2 pair directly, and the router enforces that swap's own
|
||||
minimum. The decoder checks that the address paid is that pair, deriving it as
|
||||
the router does from the V2 factory and pair init code hash the network's
|
||||
routers were deployed with and the first two tokens of that step's path; a
|
||||
payment to any other address is named. Every other payment counts, in
|
||||
whichever step and of whatever token. The minimum reads `Min. received` only
|
||||
when some step pays the transaction's sender (the From address on TxApproval,
|
||||
the sender the explorer reports on TransactionDetail), no step pays any other
|
||||
address, no step that may pay an address went unread, and the output does not
|
||||
stay in the router. Otherwise it reads `Min. paid out`, a `Paid to` line after
|
||||
`Token Out` names each other address paid, one reads
|
||||
`The router, where it stays (no later step pays it out)` when the output stays
|
||||
in the router, and one more reads `Unknown (could not be decoded)` when such a
|
||||
step went unread, or `Unknown (not named in the calldata)` when no step pays
|
||||
|
||||
+72
-13
@@ -1,7 +1,15 @@
|
||||
// Decode Uniswap Universal Router execute() calldata into human-readable
|
||||
// swap details. Designed to be extended with other DEX decoders later.
|
||||
|
||||
const { Interface, AbiCoder, getBytes, formatUnits } = require("ethers");
|
||||
const {
|
||||
Interface,
|
||||
AbiCoder,
|
||||
getBytes,
|
||||
formatUnits,
|
||||
getCreate2Address,
|
||||
keccak256,
|
||||
solidityPacked,
|
||||
} = require("ethers");
|
||||
const { truncateAmountNeverZero } = require("./amountDisplay");
|
||||
const {
|
||||
resolveTokenDecimals,
|
||||
@@ -65,6 +73,53 @@ function routerCommandTypeMask(address, chainId) {
|
||||
return router ? router[1] : null;
|
||||
}
|
||||
|
||||
// The Uniswap V2 factory and pair init code hash the Universal Routers on each
|
||||
// network were deployed with. A V2 swap step swaps through the pair its path
|
||||
// starts at, which the router finds as `UniswapV2Library.pairFor()` does: the
|
||||
// CREATE2 address from the factory, with the hash of the path's first two
|
||||
// tokens, sorted, as salt.
|
||||
// - pairFor(): https://github.com/Uniswap/universal-router/blob/802fe4c18f47300e0f183e2a42e9146ec2ea9fc3/contracts/modules/uniswap/v2/UniswapV2Library.sol
|
||||
// - mainnet: https://github.com/Uniswap/universal-router/blob/802fe4c18f47300e0f183e2a42e9146ec2ea9fc3/script/deployParameters/DeployMainnet.s.sol
|
||||
// - Sepolia: https://github.com/Uniswap/universal-router/blob/802fe4c18f47300e0f183e2a42e9146ec2ea9fc3/script/deployParameters/DeploySepolia.s.sol
|
||||
// Every router in ROUTER_ADDRESSES was deployed with these (their constructor
|
||||
// arguments on the explorer), except Sepolia's UniversalRouterV1_2_V2Support,
|
||||
// whose init code hash is zero. On Sepolia neither hash is that of the
|
||||
// factory's own pairs, so no contract sits at a pair any router there finds,
|
||||
// and every V2 swap step they run reverts.
|
||||
const V2_FACTORIES = new Map([
|
||||
[
|
||||
NETWORKS.mainnet.chainId,
|
||||
{
|
||||
v2Factory: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
|
||||
pairInitCodeHash:
|
||||
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f",
|
||||
},
|
||||
],
|
||||
[
|
||||
NETWORKS.sepolia.chainId,
|
||||
{
|
||||
v2Factory: "0xB7f907f7A9eBC822a80BD25E224be42Ce0A698A0",
|
||||
pairInitCodeHash:
|
||||
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f",
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
// The address of the V2 pair of `tokenA` and `tokenB` on the network with this
|
||||
// chain id, found as the router finds it (see V2_FACTORIES).
|
||||
function v2PairFor(chainId, tokenA, tokenB) {
|
||||
const { v2Factory, pairInitCodeHash } = V2_FACTORIES.get(chainId);
|
||||
const [token0, token1] =
|
||||
tokenA.toLowerCase() < tokenB.toLowerCase()
|
||||
? [tokenA, tokenB]
|
||||
: [tokenB, tokenA];
|
||||
return getCreate2Address(
|
||||
v2Factory,
|
||||
keccak256(solidityPacked(["address", "address"], [token0, token1])),
|
||||
pairInitCodeHash,
|
||||
);
|
||||
}
|
||||
|
||||
// Universal Router command types, each read from its command byte with the
|
||||
// router's mask (see ROUTER_ADDRESSES)
|
||||
const COMMAND_NAMES = {
|
||||
@@ -295,6 +350,7 @@ function decodeV2SwapExactIn(input) {
|
||||
recipient: d[0],
|
||||
amountIn: d[1] === 0n ? ALREADY_PAID : d[1],
|
||||
amountOutMin: d[2],
|
||||
path: d[3],
|
||||
tokenIn: d[3][0],
|
||||
tokenOut: d[3][d[3].length - 1],
|
||||
};
|
||||
@@ -786,14 +842,15 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
paidElsewhere.push(recipient);
|
||||
}
|
||||
};
|
||||
// A swap step that pays `token` to an address the very next step
|
||||
// spends, as a V2 exact-in of that token whose input is already paid,
|
||||
// is a hop, not a payout: Uniswap's mixed routes pay the next V2 pair
|
||||
// directly, and the router enforces that next swap's own minimum. Its
|
||||
// input is already paid when its amountIn is ALREADY_PAID, or
|
||||
// A swap step or V4 TAKE that pays `token` to `recipient` is a hop,
|
||||
// not a payout, when the very next step is a V2 exact-in whose input
|
||||
// is already paid, whose path starts at `token`, and whose pair (see
|
||||
// v2PairFor()) is `recipient`: Uniswap's mixed routes pay the next V2
|
||||
// pair directly, and the router enforces that next swap's own minimum.
|
||||
// Its input is already paid when its amountIn is ALREADY_PAID, or
|
||||
// CONTRACT_BALANCE, which Uniswap's routes send and with which the
|
||||
// router adds only what it holds.
|
||||
const isHop = (i, token) => {
|
||||
const isHop = (i, token, recipient) => {
|
||||
if (i + 1 >= commandsBytes.length) return false;
|
||||
if ((commandsBytes[i + 1] & commandTypeMask) !== 0x08) return false;
|
||||
const next = decodeV2SwapExactIn(inputs[i + 1]);
|
||||
@@ -802,8 +859,10 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
(next.amountIn === ALREADY_PAID ||
|
||||
next.amountIn === CONTRACT_BALANCE) &&
|
||||
present(token) &&
|
||||
present(next.tokenIn) &&
|
||||
next.tokenIn.toLowerCase() === token.toLowerCase()
|
||||
next.path.length >= 2 &&
|
||||
next.tokenIn.toLowerCase() === token.toLowerCase() &&
|
||||
recipient.toLowerCase() ===
|
||||
v2PairFor(chainId, next.path[0], next.path[1]).toLowerCase()
|
||||
);
|
||||
};
|
||||
|
||||
@@ -847,7 +906,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
// the last swap step determines the final output token
|
||||
// and minimum received amount.
|
||||
setOutput(s.tokenOut, s.amountOutMin);
|
||||
if (!isHop(i, s.tokenOut)) {
|
||||
if (!isHop(i, s.tokenOut, s.recipient)) {
|
||||
addPayee(s.recipient, s.tokenOut);
|
||||
}
|
||||
} else {
|
||||
@@ -858,7 +917,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
if (s) {
|
||||
setInputOnce(s.tokenIn, s.amountIn);
|
||||
setOutput(s.tokenOut, s.amountOutMin);
|
||||
if (!isHop(i, s.tokenOut)) {
|
||||
if (!isHop(i, s.tokenOut, s.recipient)) {
|
||||
addPayee(s.recipient, s.tokenOut);
|
||||
}
|
||||
} else {
|
||||
@@ -871,7 +930,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
if (s) {
|
||||
setInputOnce(s.tokenIn, s.amountInMax);
|
||||
setOutput(s.tokenOut, s.amountOut);
|
||||
if (!isHop(i, s.tokenOut)) {
|
||||
if (!isHop(i, s.tokenOut, s.recipient)) {
|
||||
addPayee(s.recipient, s.tokenOut);
|
||||
}
|
||||
} else {
|
||||
@@ -902,7 +961,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
const v4 = decodeV4Swap(inputs[i]);
|
||||
if (v4) {
|
||||
for (const t of v4.takes) {
|
||||
if (!isHop(i, t.currency)) {
|
||||
if (!isHop(i, t.currency, t.recipient)) {
|
||||
addPayee(t.recipient, t.currency);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-17
@@ -14,27 +14,25 @@ const DAI_ADDR = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
|
||||
const SEPOLIA_WETH_ADDR = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14";
|
||||
const USER_ADDR = "0x66133E8ea0f5D1d612D2502a968757D1048c214a";
|
||||
|
||||
// AutistMask's first-ever swap, 2026-02-27.
|
||||
// AutistMask's first-ever swap, 2026-02-27, sent from USER_ADDR.
|
||||
// Swapped USDT for ETH via Uniswap V4 Universal Router.
|
||||
// https://etherscan.io/tx/0x6749f50c4e8f975b6d14780d5f539cf151d1594796ac49b7d6a5348ba0735e77
|
||||
// This is that transaction's input, as a public Ethereum node returns it.
|
||||
const FIRST_SWAP_CALLDATA =
|
||||
"0x3593564c" +
|
||||
"000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0" +
|
||||
"0000000000000000000000000000000000000000000000000000000069a1550f00000000000000000000000000000000000000000000000000000000000000020a10000000000000000000000000000000000000000000000000000000000000" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c0" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000ffffffffffffffffffffffffffffffffffffffff" +
|
||||
"0000000000000000000000000000000000000000000000000000000069c8daf6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af" +
|
||||
"0000000000000000000000000000000000000000000000000000000069a154fe00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000041" +
|
||||
"230249bb7133205db7b2389b587c723cc182302907b9545dc40c59c33ad1d53078a65732f4182fedbc0d9d85c51d580bdc93db3556fac38f18e140da47d0eb631c00000000000000000000000000000000000000000000000000000000000000" +
|
||||
"000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000069a1550f0000000000000000000000000000000000000000000000000000000000000002" +
|
||||
"0a100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c0" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000069c8daf6" +
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000000000000000069a154fe00000000000000000000000000000000000000000000000000000000000000e0" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000041230249bb7133205db7b2389b587c723cc182302907b9545dc40c59c33ad1d53078a65732f4182fedbc0d9d85c51d580bdc93db3556fac38f18e140da47d0eb631c00000000000000000000000000000000000000000000000000000000000000" +
|
||||
"00000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003" +
|
||||
"070b0e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000220" +
|
||||
"00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000007a1200000000000000000000000000000000000000000000000000000dcb050d338e7" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
|
||||
"dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +
|
||||
"66133e8ea0f5d1d612d2502a968757d1048c214a0000000000000000000000000000000000000000000000000000000000000000756e6978000000000012";
|
||||
"0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000007a1200000000000000000000000000000000000000000000000000000dcb050d338e70000000000000000000000000000000000000000000000000000000000000001" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000001" +
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060" +
|
||||
"000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060" +
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066133e8ea0f5d1d612d2502a968757d1048c214a0000000000000000000000000000000000000000000000000000000000000000756e6978000000000012";
|
||||
|
||||
const coder = AbiCoder.defaultAbiCoder();
|
||||
const routerIface = new Interface([
|
||||
@@ -362,11 +360,11 @@ describe("uniswap decoder", () => {
|
||||
expect(result.details.find((d) => d.label === "Amount").value).toBe(
|
||||
"Unlimited",
|
||||
);
|
||||
// This fixture's TAKE parameters are one byte long, so who it pays
|
||||
// cannot be read, and the minimum is not said to be received.
|
||||
// Its TAKE pays the From address, so the minimum is received.
|
||||
expect(
|
||||
result.details.find((d) => d.label === "Min. paid out").value,
|
||||
result.details.find((d) => d.label === "Min. received").value,
|
||||
).toBe("0.0002 ETH");
|
||||
expect(labels).not.toContain("Paid to");
|
||||
|
||||
// Real mainnet calldata is in the deployed shape, so its V4 step is
|
||||
// not said to be undecoded.
|
||||
|
||||
+113
-16
@@ -424,18 +424,24 @@ describe("an output left in the router is not received", () => {
|
||||
|
||||
// Uniswap's mixed routes pay a V2 pair directly when the next step swaps
|
||||
// through it. That V2 swap spends what the pair holds: its amountIn is
|
||||
// CONTRACT_BALANCE, paid from the router, or ALREADY_PAID.
|
||||
// CONTRACT_BALANCE, paid from the router, or ALREADY_PAID. The pairs below
|
||||
// are the addresses the routers find for the first two tokens of that V2
|
||||
// swap's path.
|
||||
describe("a swap paid to the pair the next V2 swap spends", () => {
|
||||
const PAIR = "0x5555555555555555555555555555555555555555";
|
||||
// The WETH/USDC pair on Ethereum Mainnet, as its V2 factory reports it.
|
||||
const PAIR = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc";
|
||||
const CONTRACT_BALANCE = 2n ** 255n;
|
||||
const swapToPair = v3SwapExactIn(PAIR, USDT_2000, 0n, USDT, WETH);
|
||||
const AMOUNTS_IN = [
|
||||
{ kind: "CONTRACT_BALANCE", amountIn: CONTRACT_BALANCE },
|
||||
{ kind: "ALREADY_PAID", amountIn: 0n },
|
||||
];
|
||||
const swapTo = (recipient) =>
|
||||
v3SwapExactIn(recipient, USDT_2000, 0n, USDT, WETH);
|
||||
const swapToPair = swapTo(PAIR);
|
||||
const pairSpends = (amountIn, path) =>
|
||||
v2Swap(0x08, MSG_SENDER, amountIn, USDC_1990, path, false);
|
||||
|
||||
test.each([
|
||||
{ kind: "CONTRACT_BALANCE", amountIn: CONTRACT_BALANCE },
|
||||
{ kind: "ALREADY_PAID", amountIn: 0n },
|
||||
])(
|
||||
test.each(AMOUNTS_IN)(
|
||||
"a V3 swap, then a V2 swap whose amountIn is $kind, reads as a swap paid to the user",
|
||||
({ amountIn }) => {
|
||||
const result = decode(
|
||||
@@ -450,22 +456,113 @@ describe("a swap paid to the pair the next V2 swap spends", () => {
|
||||
},
|
||||
);
|
||||
|
||||
test("a V4 step whose TAKE pays the pair, then the V2 swap, reads the same", () => {
|
||||
const result = decode(
|
||||
test.each(AMOUNTS_IN)(
|
||||
"a V3 swap paid to another address, then a V2 swap whose amountIn is $kind, names that address",
|
||||
({ amountIn }) => {
|
||||
const result = decode(
|
||||
execute(swapTo(OTHER), pairSpends(amountIn, [WETH, USDC])),
|
||||
);
|
||||
expect(labels(result)).toEqual(PAID_ELSEWHERE);
|
||||
expect(paidTo(result)).toEqual([OTHER]);
|
||||
expect(detail(result, "Min. paid out").value).toBe(
|
||||
"1990.0000 USDC",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.each([
|
||||
{ recipient: PAIR, lines: PAID_TO_USER, paid: [] },
|
||||
{ recipient: OTHER, lines: PAID_ELSEWHERE, paid: [OTHER] },
|
||||
])(
|
||||
"a V4 step whose TAKE pays $recipient, then the V2 swap",
|
||||
({ recipient, lines, paid }) => {
|
||||
const result = decode(
|
||||
execute(
|
||||
v4Swap(
|
||||
SWAP_USDT_FOR_USDC,
|
||||
SETTLE_USDT,
|
||||
take(USDC, recipient),
|
||||
),
|
||||
v2Swap(
|
||||
0x08,
|
||||
MSG_SENDER,
|
||||
CONTRACT_BALANCE,
|
||||
ONE_WETH,
|
||||
[USDC, WETH],
|
||||
false,
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(labels(result)).toEqual(lines);
|
||||
expect(paidTo(result)).toEqual(paid);
|
||||
},
|
||||
);
|
||||
|
||||
describe("on Sepolia", () => {
|
||||
const SEPOLIA = "0xaa36a7";
|
||||
// UniversalRouterV2_1_2 on Sepolia.
|
||||
const SEPOLIA_ROUTER = "0x7E4f6c5e954Da5c61B3423D81E2277431Ac043f3";
|
||||
// The WETH/USDC pair the routers on Sepolia find. Sepolia's V2
|
||||
// factory reports another address for it, and no contract sits at
|
||||
// this one (see V2_FACTORIES in src/shared/uniswap.js).
|
||||
const SEPOLIA_PAIR = "0x541b50d32B73E769ac42F0FddD86D15FcAa861Fb";
|
||||
const SEPOLIA_UNI = "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984";
|
||||
const SEPOLIA_WETH = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14";
|
||||
const SEPOLIA_USDC = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238";
|
||||
// UNI for WETH, paid to `recipient`, then WETH for USDC through the
|
||||
// pair.
|
||||
const route = (recipient, amountIn) =>
|
||||
execute(
|
||||
v4Swap(SWAP_USDT_FOR_USDC, SETTLE_USDT, take(USDC, PAIR)),
|
||||
v3SwapExactIn(
|
||||
recipient,
|
||||
ONE_WETH,
|
||||
0n,
|
||||
SEPOLIA_UNI,
|
||||
SEPOLIA_WETH,
|
||||
),
|
||||
v2Swap(
|
||||
0x08,
|
||||
MSG_SENDER,
|
||||
CONTRACT_BALANCE,
|
||||
ONE_WETH,
|
||||
[USDC, WETH],
|
||||
amountIn,
|
||||
USDC_1990,
|
||||
[SEPOLIA_WETH, SEPOLIA_USDC],
|
||||
false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
test.each(AMOUNTS_IN)(
|
||||
"paid to the pair, then a V2 swap whose amountIn is $kind, reads as a swap paid to the user",
|
||||
({ amountIn }) => {
|
||||
const result = uniswap.decode(
|
||||
route(SEPOLIA_PAIR, amountIn),
|
||||
SEPOLIA_ROUTER,
|
||||
SEPOLIA,
|
||||
{},
|
||||
USER,
|
||||
);
|
||||
expect(labels(result)).toEqual(PAID_TO_USER);
|
||||
},
|
||||
);
|
||||
expect(labels(result)).toEqual(PAID_TO_USER);
|
||||
expect(detail(result, "Min. received").value).toBe("1.0000 WETH");
|
||||
|
||||
test.each(AMOUNTS_IN)(
|
||||
"paid to another address, then a V2 swap whose amountIn is $kind, names that address",
|
||||
({ amountIn }) => {
|
||||
const result = uniswap.decode(
|
||||
route(OTHER, amountIn),
|
||||
SEPOLIA_ROUTER,
|
||||
SEPOLIA,
|
||||
{},
|
||||
USER,
|
||||
);
|
||||
expect(labels(result)).toEqual(PAID_ELSEWHERE);
|
||||
expect(paidTo(result)).toEqual([OTHER]);
|
||||
},
|
||||
);
|
||||
|
||||
test("the same route on Ethereum Mainnet names the Sepolia pair", () => {
|
||||
const result = decode(route(SEPOLIA_PAIR, 0n));
|
||||
expect(paidTo(result)).toEqual([SEPOLIA_PAIR]);
|
||||
});
|
||||
});
|
||||
|
||||
test.each([
|
||||
|
||||
Reference in New Issue
Block a user