fix: correct swap display — output token, min received, from address, and amount #128
No reviewers
Labels
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: sneak/AutistMask#128
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/issue-127-swap-amount-display"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fix multi-step Uniswap swap decoding and transaction display.
Changes
1. uniswap.js — Last swap step wins for output
In multi-step swaps (e.g. V3 Swap → V4 Swap), the output token and min received amount now come from the last swap step instead of the first. Previously, an intermediate token's
amountOutMin(18 decimals) was formatted with the final token's decimals (6), producing astronomically wrong "Min. received" values (~2 trillion USDC for a $2 swap).2. transactions.js — Consolidate swap token transfers
Contract call token transfers (swaps) are now consolidated into the original transaction entry instead of creating separate entries per token transfer. This prevents intermediate hop tokens (e.g. USDS in a USDT→USDS→USDC route) from appearing as the transaction's Amount. The received token (swap output) is preferred for the display amount.
3. transactions.js — Preserve original from/to
The original transaction's from/to addresses are preserved for contract calls, so the user sees their own address instead of a router or Permit2 contract address.
Issues Fixed
closes #127
Summary of Changes
This PR fixes four display bugs in the transaction detail view for multi-step Uniswap swaps (e.g. USDT → USDS → USDC via V3 Swap → V4 Swap):
Files Changed
src/shared/uniswap.js— Fixed output token resolution for multi-step swapsoutputTokenandminOutputfrom first-wins to last-wins semantics indecode()src/shared/transactions.js— Fixed token transfer merging for contract callsfrom/toare preserved so the user sees their own address, not Permit2/router contractsRoot Cause
Multi-step swaps produce multiple token transfers (input → intermediate → output). The previous code created separate transaction list entries for each, and the Uniswap decoder used first-wins for output fields, causing the intermediate step's values to be displayed with the final token's decimal formatting.
Code Review — PASS ✅
Reviewer: clawbot (independent review)
Analysis
uniswap.js — last-wins semantics: Correct. In multi-step swaps (V3→V4), the user's input comes from the first step and their output comes from the last step. The change correctly keeps
inputToken/inputAmountas first-wins while makingoutputToken/minOutputlast-wins. This works for:transactions.js — consolidation: Correct. Contract call token transfers are merged into the original tx entry rather than spawning separate entries. The received transfer (swap output) is preferred for display amount via
isReceivedcheck, with fallback to sent transfer if no received exists. Original from/to preserved viacontinue(skips overwriting). Edge cases handled:All four reported issues addressed:
Scope: Clean — only 2 files changed, no CI/linter/test/Makefile modifications.
Docker build: ✅ passes
README consistency: No documentation changes needed — this is a bug fix, not a behavior change.
Security: Display correctness is critical for a wallet. This fix improves accuracy of displayed amounts and addresses, reducing risk of user confusion.
Review: PASS ✅
Reviewed the diff (2 files, +37/-21) against issue #127.
uniswap.js — "last swap step wins" for output
The change from "first wins" (
if (!outputToken)) to "always overwrite" foroutputTokenandminOutputacross V3, V2, and V4 swap decoders is correct. In a multi-step swap like V3→V4 (e.g. USDT→WETH→USDC), the first step's output is an intermediate token, not what the user receives. Taking the last step's output correctly resolves:amountOutMin(18 decimals) was being formatted with the final token's decimals (6), producing the ~2 trillion "Min. received" value.inputTokenandinputAmountcorrectly retain "first wins" semantics (the first step has the actual input).Minor note: V4 uses
if (v4.amountOutMin)which would skipBigInt(0)(falsy), but this is pre-existing behavior and a zero-slippage swap is an extreme edge case — not introduced by this PR.transactions.js — Consolidate swap token transfers
Instead of creating separate entries per token transfer (which caused intermediate hop tokens like USDS to appear as the transaction Amount), contract call token transfers are now consolidated into the original tx entry. The logic correctly:
isReceivedcheckneedsAmountfrom/toaddresses (user's address and called contract), fixing the Permit2 address display bugcontinueto skip adding duplicatesBoth orderings of token transfers (sent-first or received-first) produce the correct result since received always overwrites.
Scope check
docker build .(includesmake check) passes ✓All four bugs from #127 are addressed. Marking
merge-ready.