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.
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.
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 next2026-08-23 16:31:03 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #346
The defect
In
src/shared/uniswap.jstheToken Outdetail line was pushed onlyif (outSymbol). A token absent from the bundled list has no symbol, so the line was dropped entirely — the dApp approval screen showed aMin. receivedfigure 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 Inline in this same file already made. One condition, no new presentation:Token Out= the address,isToken: true, so the popup rendersUnknown tokenabove the address block, as it does forToken In.SYMBOL (0x...).UNWRAP_WETH: unchanged,ETH, no address.Composition with #340 (same screen, same affected population): for a token nothing knows the scale of,
Token Outnames the address andMin. receivedreads1000000000 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 exactsrc/change reverted,tests/uniswapTokenOut.test.jsleft in place — thenmake test.undefinedbecausedetails.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 checkgreen ona1e9d0b, rebased onnextat43784ca:Tests: 895 passed, 895 total(47 suites) — no(cached)line; jest ran the suites.test-verify-build: 46 case(s) passedcheck-censored: 165 tracked file(s) inspectedlintlayer executed rather than reportingCACHED:#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 fmtrun;TODO.mdupdated in the same commit.Note
tests/uniswapTokenOut.test.jsbuilds its unlisted-token constant throughgetAddress(): the ABI decode hands back a checksummed address, so that is the form the line carries.PASS — DoD of #346 met; fail-first reproduced in an independent clone (3 failed / 892 passed with only
src/shared/uniswap.jsreverted, the 2 regression tests in the new file passing either way),make checkgreen with thelintstage executing (notCACHED), all three CI contexts green ona1e9d0b, noToken Outpath regressed.Non-blocking, pre-existing, out of scope for this issue: symbol resolution (
tokenInfo()insrc/shared/uniswap.js,tokenLabel()insrc/popup/views/approval.js) reads only the bundled list, so a token the user tracks with a symbol still renders asUnknown tokenabove its address while #340 correctly takes its decimals from that same tracked entry. Worth a follow-up issue; not a defect of this change.