fix: always name a swap's output token, by address when no symbol is known (closes #346) #352

Merged
clawbot merged 1 commits from fix/346-swap-token-out-line into next 2026-08-23 16:31:03 +02:00
Collaborator

Closes #346

The defect

In src/shared/uniswap.js the Token Out detail line was pushed only if (outSymbol). A token absent from the bundled list has no symbol, so the line was dropped entirely — the dApp approval screen showed a Min. received figure with no statement of which token is being received. Affects every token not in the bundled list, which is every newly listed token.

The fix

The line is now keyed on the resolved token address, with the address as the label when there is no symbol — the same fallback the Token In line in this same file already made. One condition, no new presentation:

  • unlisted token out: Token Out = the address, isToken: true, so the popup renders Unknown token above the address block, as it does for Token In.
  • bundled token out: unchanged, SYMBOL (0x...).
  • native ETH out, including via UNWRAP_WETH: unchanged, ETH, no address.

Composition with #340 (same screen, same affected population): for a token nothing knows the scale of, Token Out names the address and Min. received reads 1000000000 base units (decimals unknown). The address says which token, the figure says how much and declares the scale unknown — no address stands next to a number that silently means something else. Covered by a test. When the scale is known (tracked token) but the symbol is not, the line is still the address and the amount is a real quantity.

Fail-first evidence

Mutation: git stash push src/shared/uniswap.js — the exact src/ change reverted, tests/uniswapTokenOut.test.js left in place — then make test.

Test Suites: 1 failed, 46 passed, 47 total
Tests:       3 failed, 892 passed, 895 total

  a swap to a token absent from the bundled list
    x still renders a Token Out line, naming the address (19 ms)
    x names the address alongside the unknown-scale refusal (8 ms)
    x names the address when the scale is known but the symbol is not (6 ms)

  > 65 |         expect(out).toBeDefined();
    expect(received).toBeDefined()
    Received: undefined
    TypeError: Cannot read properties of undefined (reading 'value')

undefined because details.find((d) => d.label === "Token Out") finds nothing: the line is absent, which is the defect. The two regression tests in the same file (bundled token, native ETH) passed against head, so the change is additive.

With the fix: Test Suites: 47 passed, Tests: 895 passed.

Verification

make check green on a1e9d0b, rebased on next at 43784ca:

  • Tests: 895 passed, 895 total (47 suites) — no (cached) line; jest ran the suites.
  • test-verify-build: 46 case(s) passed
  • check-censored: 165 tracked file(s) inspected
  • lint in the pinned container — the lint layer executed rather than reporting CACHED: #11 [lint 1/1] RUN make lint / $ eslint . && prettier --check . / All matched files use Prettier code style! / #11 DONE 4.9s. No lint was run on the host.
  • make fmt run; TODO.md updated in the same commit.

Note

tests/uniswapTokenOut.test.js builds its unlisted-token constant through getAddress(): the ABI decode hands back a checksummed address, so that is the form the line carries.

Closes https://git.eeqj.de/sneak/AutistMask/issues/346 ## The defect In `src/shared/uniswap.js` the `Token Out` detail line was pushed only `if (outSymbol)`. A token absent from the bundled list has no symbol, so the line was dropped entirely — the dApp approval screen showed a `Min. received` figure with no statement of which token is being received. Affects every token not in the bundled list, which is every newly listed token. ## The fix The line is now keyed on the resolved token address, with the address as the label when there is no symbol — the same fallback the `Token In` line in this same file already made. One condition, no new presentation: - unlisted token out: `Token Out` = the address, `isToken: true`, so the popup renders `Unknown token` above the address block, as it does for `Token In`. - bundled token out: unchanged, `SYMBOL (0x...)`. - native ETH out, including via `UNWRAP_WETH`: unchanged, `ETH`, no address. Composition with https://git.eeqj.de/sneak/AutistMask/issues/340 (same screen, same affected population): for a token nothing knows the scale of, `Token Out` names the address and `Min. received` reads `1000000000 base units (decimals unknown)`. The address says which token, the figure says how much and declares the scale unknown — no address stands next to a number that silently means something else. Covered by a test. When the scale *is* known (tracked token) but the symbol is not, the line is still the address and the amount is a real quantity. ## Fail-first evidence Mutation: `git stash push src/shared/uniswap.js` — the exact `src/` change reverted, `tests/uniswapTokenOut.test.js` left in place — then `make test`. ``` Test Suites: 1 failed, 46 passed, 47 total Tests: 3 failed, 892 passed, 895 total a swap to a token absent from the bundled list x still renders a Token Out line, naming the address (19 ms) x names the address alongside the unknown-scale refusal (8 ms) x names the address when the scale is known but the symbol is not (6 ms) > 65 | expect(out).toBeDefined(); expect(received).toBeDefined() Received: undefined TypeError: Cannot read properties of undefined (reading 'value') ``` `undefined` because `details.find((d) => d.label === "Token Out")` finds nothing: the line is absent, which is the defect. The two regression tests in the same file (bundled token, native ETH) passed against head, so the change is additive. With the fix: `Test Suites: 47 passed`, `Tests: 895 passed`. ## Verification `make check` green on `a1e9d0b`, rebased on `next` at `43784ca`: - `Tests: 895 passed, 895 total` (47 suites) — no `(cached)` line; jest ran the suites. - `test-verify-build: 46 case(s) passed` - `check-censored: 165 tracked file(s) inspected` - lint in the pinned container — the `lint` layer executed rather than reporting `CACHED`: `#11 [lint 1/1] RUN make lint` / `$ eslint . && prettier --check .` / `All matched files use Prettier code style!` / `#11 DONE 4.9s`. No lint was run on the host. - `make fmt` run; `TODO.md` updated in the same commit. ## Note `tests/uniswapTokenOut.test.js` builds its unlisted-token constant through `getAddress()`: the ABI decode hands back a checksummed address, so that is the form the line carries.
clawbot added the needs-review label 2026-08-23 16:25:10 +02:00
clawbot added 1 commit 2026-08-23 16:25:10 +02:00
fix: always name a swap's output token, by address when no symbol is known (closes #346)
All checks were successful
check / check (push) Successful in 30s
e2e / e2e-chrome (push) Successful in 1m43s
e2e / e2e-firefox (push) Successful in 27s
a1e9d0b7d9
The Token Out detail line was pushed only when the bundled token list supplied a symbol, so a swap whose output token is absent from that list — which is every newly listed token — rendered no Token Out line at all, leaving a Min. received figure with no statement of which token is being received. The line is now keyed on the token's address and falls back to showing it when there is no symbol, the same fallback the Token In line in this file already made; the symbol-only case (native ETH, including an unwrap) is unchanged. This composes with the unknown-scale refusal on the same screen: for a token nothing knows the scale of, the address names the token and the base-unit figure states the amount with the scale declared unknown, so no address stands next to a number that silently means something else.
Author
Collaborator

PASS — DoD of #346 met; fail-first reproduced in an independent clone (3 failed / 892 passed with only src/shared/uniswap.js reverted, the 2 regression tests in the new file passing either way), make check green with the lint stage executing (not CACHED), all three CI contexts green on a1e9d0b, no Token Out path regressed.

Non-blocking, pre-existing, out of scope for this issue: symbol resolution (tokenInfo() in src/shared/uniswap.js, tokenLabel() in src/popup/views/approval.js) reads only the bundled list, so a token the user tracks with a symbol still renders as Unknown token above its address while #340 correctly takes its decimals from that same tracked entry. Worth a follow-up issue; not a defect of this change.

PASS — DoD of https://git.eeqj.de/sneak/AutistMask/issues/346 met; fail-first reproduced in an independent clone (3 failed / 892 passed with only `src/shared/uniswap.js` reverted, the 2 regression tests in the new file passing either way), `make check` green with the `lint` stage executing (not `CACHED`), all three CI contexts green on `a1e9d0b`, no `Token Out` path regressed. Non-blocking, pre-existing, out of scope for this issue: symbol resolution (`tokenInfo()` in `src/shared/uniswap.js`, `tokenLabel()` in `src/popup/views/approval.js`) reads only the bundled list, so a token the user tracks *with* a symbol still renders as `Unknown token` above its address while https://git.eeqj.de/sneak/AutistMask/issues/340 correctly takes its decimals from that same tracked entry. Worth a follow-up issue; not a defect of this change.
clawbot merged commit 36bc6bee0e into next 2026-08-23 16:31:03 +02:00
clawbot deleted branch fix/346-swap-token-out-line 2026-08-23 16:31:03 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#352