Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73b7db9d79 |
@@ -3170,29 +3170,26 @@ Currently supported:
|
||||
`Token In`; one of `Token In` after an amount that is not a quantity (a V4
|
||||
open delta, `ALREADY_PAID`, which keep their wording on the `Amount` line, or
|
||||
no stated amount, as of a V4 exact-out step); and one whose amount the decoder
|
||||
does not know. When the step that sets the input side states no amount and
|
||||
there is no `Amount` line, that step gets them too whenever another take's
|
||||
lines give a figure, so that figure cannot read as all that leaves. They come
|
||||
in step order, the step that sets the input side first when it has them:
|
||||
`Moved from the sender`, its amount, worded as the `Amount` line words one, or
|
||||
`Unknown (could not be decoded)` when the decoder does not know it;
|
||||
`Token moved`, giving the token's address as the `Token In` line does; and
|
||||
`Moved to`, which reads `The swap step that spends it`. A token the decoder
|
||||
does not know gives the `Moved from the sender` line alone. A step may also
|
||||
take from the user's wallet what the decoder did not read: a V4 step, other
|
||||
than the one that sets the input side, whose input or one of whose `SETTLE`s
|
||||
or `SETTLE_ALL`s it cannot read, which the router still runs; a V2 or V3
|
||||
exact-in or V2 exact-out step or a `PERMIT2_TRANSFER_FROM` whose input it
|
||||
cannot read; every `PERMIT2_TRANSFER_FROM_BATCH`; every `V3_SWAP_EXACT_OUT`,
|
||||
which the decoder does not read, wherever it stands; and every step it fails
|
||||
on. Each such step gives one `Moved from the sender` line among those, reading
|
||||
`Unknown (could not be decoded)`. That line, or any line for a token the
|
||||
decoder does not know, leaves no `Amount` line, whose figure might be too low,
|
||||
and such a step before a `PERMIT2_PERMIT` keeps the permit's amount from
|
||||
standing. Then each `PERMIT2_TRANSFER_FROM` but one into the router, in step
|
||||
order, gives three lines: `Moved from the sender`, the amount it moves, for
|
||||
example `1000000.0000 USDT`; `Token moved`, giving the token's address as the
|
||||
`Token In` line does; and `Moved to`, which reads
|
||||
does not know. They come in step order, the step that sets the input side
|
||||
first when it has them: `Moved from the sender`, its amount, worded as the
|
||||
`Amount` line words one, or `Unknown (could not be decoded)` when the decoder
|
||||
does not know it; `Token moved`, giving the token's address as the `Token In`
|
||||
line does; and `Moved to`, which reads `The swap step that spends it`. A token
|
||||
the decoder does not know gives the `Moved from the sender` line alone. A step
|
||||
may also take from the user's wallet what the decoder did not read: a V4 step,
|
||||
other than the one that sets the input side, whose input or one of whose
|
||||
`SETTLE`s or `SETTLE_ALL`s it cannot read, which the router still runs; a V2
|
||||
or V3 exact-in or V2 exact-out step or a `PERMIT2_TRANSFER_FROM` whose input
|
||||
it cannot read; every `PERMIT2_TRANSFER_FROM_BATCH`; every
|
||||
`V3_SWAP_EXACT_OUT`, which the decoder does not read, wherever it stands; and
|
||||
every step it fails on. Each such step gives one `Moved from the sender` line
|
||||
among those, reading `Unknown (could not be decoded)`. That line, or any line
|
||||
for a token the decoder does not know, leaves no `Amount` line, whose figure
|
||||
might be too low, and such a step before a `PERMIT2_PERMIT` keeps the permit's
|
||||
amount from standing. Then each `PERMIT2_TRANSFER_FROM` but one into the
|
||||
router, in step order, gives three lines: `Moved from the sender`, the amount
|
||||
it moves, for example `1000000.0000 USDT`; `Token moved`, giving the token's
|
||||
address as the `Token In` line does; and `Moved to`, which reads
|
||||
`The V2 pair the next swap spends` for a hop and `The sender` for `address(1)`
|
||||
on every router but `UniversalRouterV1`, and otherwise gives the address. A
|
||||
move into the router needs none of them, because its `Paid to` line and
|
||||
|
||||
@@ -1795,14 +1795,8 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
// (see spentUnread()), or a token it does not know.
|
||||
const spentFromSender = [];
|
||||
let total = null;
|
||||
// The take of the step that set the input side, when the total
|
||||
// starts from it and it stated no amount (see below), or null.
|
||||
let firstTakeUnstated = null;
|
||||
if (permit === null || sameAddress(inputToken, permit.token)) {
|
||||
total = inputAmount;
|
||||
if (inputEstablished && !present(inputAmount)) {
|
||||
firstTakeUnstated = { token: inputToken, amount: null };
|
||||
}
|
||||
} else if (inputEstablished) {
|
||||
// The step that set the input side spent a token other than the
|
||||
// permit's, or one not known, so the total starts with no figure.
|
||||
@@ -1824,17 +1818,6 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
||||
if (ofTokenIn && !present(t.amount)) total = null;
|
||||
}
|
||||
inputAmount = permitStands ? permit.amount : total;
|
||||
// With no Amount figure, a later take's own figure could read as all
|
||||
// that leaves, so the step that set the input side, when it stated
|
||||
// no amount, then gets lines of its own too, first, saying its amount
|
||||
// is not known.
|
||||
if (
|
||||
!present(inputAmount) &&
|
||||
firstTakeUnstated !== null &&
|
||||
spentFromSender.some((s) => present(s.amount))
|
||||
) {
|
||||
spentFromSender.unshift(firstTakeUnstated);
|
||||
}
|
||||
|
||||
// The commands of an EXECUTE_SUB_PLAN step may spend the user's tokens
|
||||
// or pay the user, before or after any other step, and a permit among
|
||||
|
||||
+19
-83
@@ -1942,9 +1942,7 @@ describe("uniswap decoder", () => {
|
||||
|
||||
// A later step's amount that cannot be added to the Amount line,
|
||||
// because the amount of the step that set the input side is not a
|
||||
// quantity, gets lines of its own. When that step stated no amount,
|
||||
// it gets lines of its own too, saying so, so the 400 USDT cannot
|
||||
// read as all that leaves.
|
||||
// quantity, gets lines of its own.
|
||||
const V2_EXACT_OUT_400 = encodeV2SwapExactOut(
|
||||
USER_ADDR,
|
||||
390000000n,
|
||||
@@ -1956,37 +1954,28 @@ describe("uniswap decoder", () => {
|
||||
["Token moved", "USDT (" + USDT_ADDR + ")"],
|
||||
["Moved to", "The swap step that spends it"],
|
||||
];
|
||||
const UNKNOWN_USDT = [
|
||||
["Moved from the sender", UNDECODED],
|
||||
["Token moved", "USDT (" + USDT_ADDR + ")"],
|
||||
["Moved to", "The swap step that spends it"],
|
||||
];
|
||||
// A V4 exact-out step of at most 600 USDT for 1 WETH, in the shape
|
||||
// of Uniswap's own routes: a SETTLE of OPEN_DELTA by the user pays
|
||||
// what the swap spends.
|
||||
const V4_EXACT_OUT_600 = encodeV4Swap(
|
||||
new Uint8Array([
|
||||
V4_SWAP_EXACT_OUT_SINGLE_ID,
|
||||
V4_SETTLE_ID,
|
||||
V4_TAKE_ALL_ID,
|
||||
]),
|
||||
[
|
||||
encodeV4ExactOutSingle(
|
||||
USDT_ADDR,
|
||||
WETH_ADDR,
|
||||
ONE_WETH,
|
||||
USDT_600,
|
||||
),
|
||||
encodeV4Settle(USDT_ADDR),
|
||||
encodeV4TakeAll(WETH_ADDR, 0n),
|
||||
],
|
||||
);
|
||||
test.each([
|
||||
{
|
||||
kind: "a V4 exact-out step with a SETTLE of OPEN_DELTA by the user",
|
||||
commands: [0x10, 0x09],
|
||||
first: V4_EXACT_OUT_600,
|
||||
lines: [...UNKNOWN_USDT, ...UP_TO_400_USDT],
|
||||
first: encodeV4Swap(
|
||||
new Uint8Array([
|
||||
V4_SWAP_EXACT_OUT_SINGLE_ID,
|
||||
V4_SETTLE_ID,
|
||||
V4_TAKE_ALL_ID,
|
||||
]),
|
||||
[
|
||||
encodeV4ExactOutSingle(
|
||||
USDT_ADDR,
|
||||
WETH_ADDR,
|
||||
ONE_WETH,
|
||||
USDT_600,
|
||||
),
|
||||
encodeV4Settle(USDT_ADDR),
|
||||
encodeV4TakeAll(WETH_ADDR, 0n),
|
||||
],
|
||||
),
|
||||
lines: UP_TO_400_USDT,
|
||||
},
|
||||
{
|
||||
kind: "a V4 exact-in step of the open delta",
|
||||
@@ -2031,59 +2020,6 @@ describe("uniswap decoder", () => {
|
||||
},
|
||||
);
|
||||
|
||||
// The V4 exact-out route above the other way round, where the later
|
||||
// take's unknown amount leaves no figure at all, and other ways the
|
||||
// step that sets the input side states no amount.
|
||||
test.each([
|
||||
{
|
||||
kind: "a V2 exact-out of at most 400 USDT, then a V4 exact-out step of at most 600 USDT",
|
||||
commands: [0x09, 0x10],
|
||||
inputs: [V2_EXACT_OUT_400, V4_EXACT_OUT_600],
|
||||
lines: UNKNOWN_USDT,
|
||||
},
|
||||
{
|
||||
kind: "a PERMIT2_PERMIT of 1 USDT to another spender, a V4 exact-out step of at most 600 USDT, then a V2 exact-out of at most 400 USDT",
|
||||
commands: [0x0a, 0x10, 0x09],
|
||||
inputs: [
|
||||
encodePermit2(USDT_ADDR, 1000000n, OTHER_ADDR),
|
||||
V4_EXACT_OUT_600,
|
||||
V2_EXACT_OUT_400,
|
||||
],
|
||||
lines: [...UNKNOWN_USDT, ...UP_TO_400_USDT],
|
||||
},
|
||||
{
|
||||
kind: "a V4 step whose swap action cannot be read, with a SETTLE of WETH by the user, then a V2 exact-out of at most 400 USDT",
|
||||
commands: [0x10, 0x09],
|
||||
inputs: [
|
||||
encodeV4Swap(
|
||||
new Uint8Array([
|
||||
V4_SWAP_EXACT_IN_SINGLE_ID,
|
||||
V4_SETTLE_ID,
|
||||
V4_TAKE_ALL_ID,
|
||||
]),
|
||||
[
|
||||
UNREAD_SWAP,
|
||||
encodeV4Settle(WETH_ADDR),
|
||||
encodeV4TakeAll(USDC_ADDR, 0n),
|
||||
],
|
||||
),
|
||||
V2_EXACT_OUT_400,
|
||||
],
|
||||
lines: [
|
||||
["Moved from the sender", UNDECODED],
|
||||
["Token moved", "WETH (" + WETH_ADDR + ")"],
|
||||
["Moved to", "The swap step that spends it"],
|
||||
...UP_TO_400_USDT,
|
||||
],
|
||||
},
|
||||
])(
|
||||
"$kind, all paid by the user, shows that an amount is not known",
|
||||
({ commands, inputs, lines }) => {
|
||||
const result = decodeSteps(commands, inputs);
|
||||
expect(linesBetweenTokens(result)).toEqual(lines);
|
||||
},
|
||||
);
|
||||
|
||||
// A V2 exact-out step makes every amount the swap steps spend a maximum,
|
||||
// the total included.
|
||||
test("a split with a V2 exact-out step the user pays for shows the total as a maximum", () => {
|
||||
|
||||
Reference in New Issue
Block a user