Compare commits

..
1 Commits
Author SHA1 Message Date
sneak 9d97fa9d66 fix: a permit on the zero address never reads as ETH (closes #660)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
Permit2 has no allowance of ETH, but the swap decoder read a permit's
zero-address token as ETH. Its Allowance token line read ETH. A permit of
1 wei of the zero address before a V4 step of 5 ETH stood as the Amount
figure, which then read 0.000000000000000001 ETH. With that permit to
another spender, there was no Amount line.

Such a permit now never stands as the Amount figure. To another spender,
it no longer removes the Amount line either. Its lines give the amount in
base units and name the zero address, as for a token nothing knows.

Model: opus-5-5
2026-10-27 03:28:19 +00:00
2 changed files with 18 additions and 18 deletions
+10 -10
View File
@@ -3500,16 +3500,16 @@ Currently supported:
later permit or batch may raise what Permit2 lets the router take, and so may later permit or batch may raise what Permit2 lets the router take, and so may
a step the decoder does not read in full: a command the decoder does not read a step the decoder does not read in full: a command the decoder does not read
(listed at the end of this section), a step whose input it cannot read, in (listed at the end of this section), a step whose input it cannot read, in
whole or in part, and a step it fails on. Every other `PERMIT2_PERMIT`, before whole or in part, and a step it fails on. A permit on the zero address never
the step that sets the input side or after it, leaves `Token In` and the stands as the `Amount` figure, because Permit2 has no allowance of ETH. Every
`Amount` line as they read with no permit, unless it lets a spender other than other `PERMIT2_PERMIT`, before the step that sets the input side or after it,
the router take `Token In` (see below): a permit of 5,000 DAI to another leaves `Token In` and the `Amount` line as they read with no permit, unless it
address and then a V3 exact-in of 600 USDT read `Swap USDT → USDC` with lets a spender other than the router take `Token In` (see below): a permit of
`Amount 600.0000 USDT`, and so do that exact-in and then a permit of 5,000 DAI 5,000 DAI to another address and then a V3 exact-in of 600 USDT read
to the router. A permit on the zero address never stands as the `Amount` `Swap USDT → USDC` with `Amount 600.0000 USDT`, and so do that exact-in and
figure, because Permit2 has no allowance of ETH. When a permit's amount stands then a permit of 5,000 DAI to the router. When a permit's amount stands and
and the step that sets the input side spends a token other than the permit's, the step that sets the input side spends a token other than the permit's, or
or one the decoder does not know, that step's amount gets lines of its own, one the decoder does not know, that step's amount gets lines of its own,
unless it states no amount and the user pays it none of its input token. Each unless it states no amount and the user pays it none of its input token. Each
take the total does not count gives three lines after the `Amount` line (after take the total does not count gives three lines after the `Amount` line (after
`Token In` when there is none): one of a token other than `Token In`; one of `Token In` when there is none): one of a token other than `Token In`; one of
+8 -8
View File
@@ -2031,15 +2031,15 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
// before it takes under whatever allowance the router already had, so // before it takes under whatever allowance the router already had, so
// it may take more. A later permit or batch, or a later step this // it may take more. A later permit or batch, or a later step this
// decoder did not read, may raise what Permit2 lets the router take. // decoder did not read, may raise what Permit2 lets the router take.
// A permit on the zero address never stands as the Amount figure,
// because Permit2 has no allowance of ETH.
// Every other PERMIT2_PERMIT leaves Token In and the Amount line as // Every other PERMIT2_PERMIT leaves Token In and the Amount line as
// they read with no permit, and gets lines of its own below. A permit // they read with no permit, and gets lines of its own below. A step
// on the zero address never stands as the Amount figure, because // that may take from the sender what this decoder did not read leaves
// Permit2 has no allowance of ETH. A step that may take from the // no figure, permit or not (see spentUnread()), and so do a permit
// sender what this decoder did not read leaves no figure, permit or // or batch whose input it cannot read (see permitUnread()) and an
// not (see spentUnread()), and so do a permit or batch whose input it // allowance of Token In to another spender (see toAnotherSpender). An
// cannot read (see permitUnread()) and an allowance of Token In to // EXECUTE_SUB_PLAN step leaves both sides unknown (below).
// another spender (see toAnotherSpender). An EXECUTE_SUB_PLAN step
// leaves both sides unknown (below).
const stepCommands = Array.from( const stepCommands = Array.from(
commandsBytes, commandsBytes,
(b) => commands[b & commandTypeMask], (b) => commands[b & commandTypeMask],