Repository navigation
Conversation
The swap Route sheet opens from Details and needs a back arrow on the left with the X on the right. onBack adds the arrow to the centered layout only; the other layouts are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
buildSwapRouteView turns routes[0] of /swap/quotes into what the Route
sheet shows:
- share: the path's fraction as a whole percent ("0.32" -> "32%", "<1%"
for a nonzero share that rounds to 0). Null on a single-path route.
- poolCount: the path's hop count, only when its hops carry `pool`. Classic
hops never do (Horizon doesn't name the pool), so classic shows none.
- providers: the distinct venues across all paths, first-seen order.
- venue icons: matched by name against /protocols (useProtocolsStore,
already loaded at app start for Discover), case-insensitively. An
unlisted venue such as stellar-dex has no icon URL and gets a generic
icon.
- tokens: XLM, CODE:ISSUER, or a C... contract. A contract token takes
the code of a held balance with that contract ID, or the destination
descriptor's code; otherwise it shows truncateAddress(id), as the C3
picker subtitle does. Icons are read from the existing pipeline only;
nothing writes to the shared icon cache.
Returns null with no quote or no path with hops, which hides the Route
row. Display only: nothing at signing reads it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C1-C3 had no details sheet on the amount screen, so this adds the entry point from Figma 2863:148115: "<fee> network fee · See details (i)" under the receive card, shown once a quote is on screen. It opens Details: network fee (XLM, plus fiat when it is at least a cent), rate (the review sheet's sticky rate), and Route: "N providers" with stacked venue icons and a chevron. The Route row is hidden when the quote has no route. Route (Figma 2983:153139, expanded 4241:50151) replaces the Details content in the same sheet. Back returns to Details; X closes. It shows the sold -> bought tokens, "Split across N paths", one row per path (share pill, up to 3 hop venue icons then "+N", "N pools", chevron), the hint, and the provider chips. A path expands into its tokens with the venue between each pair. No per-hop amounts: the current design dropped them. Classic stand-in (design has not drawn it yet): the same sheet, with "1 path" in place of "Split across N paths", no share pill on a single-path route, and no "N pools" label when the hops carry no pool. Review with design. Other choices: - The fee line shows fiat when the XLM price makes it at least $0.01, and the XLM amount otherwise, since formatFiatAmount rounds a typical 0.00001 XLM fee to "$0.00". - Provider chips wrap instead of scrolling sideways, to avoid a horizontal scroller inside the sheet's scroll view. - "1 provider" / "1 pool" / "1 path" are separate keys rather than i18next plurals, which the app does not use yet. - The Rate row has no (i) icon: there is no copy for it. - Tapping See details dismisses the keyboard first, as Review does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The store keeps the last quote until the debounced re-quote (500 ms) replaces it. The See details line checked only that a pathResult existed, so for that window, after a token or amount change, Details and Route showed the previous inputs' route. The sticky rate also re-formatted the old rate under the new token's symbol, and stayed after the quote was cleared. The line, the Route data and the Rate now require pathResult.quoteKey to match the current inputs' key, as the CTA, the settings rebuild and the build already do. The rate hook only sees a current pathResult. Display only: nothing at signing reads these. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backend #185 adds an optional resourceFee (XLM) to each LI.FI route in /swap/quotes. /swap/tx's fee is the inclusion fee plus that value, so the fee shown before review left out most of what the swap pays. The See details line and the Details fee row now show swapFee plus routes[0].resourceFee with computeTotalFeeXlm, the BigNumber sum Send's FeeBreakdownBottomSheet uses. A classic route has no resourceFee and still shows swapFee. The fiat value uses the same total. The resource fee is read only for a quote matching the current inputs. When the route has a resourceFee, Details shows Send's Soroban fee note (feeBreakdown.descriptionSoroban). No new strings. Spendable/Max and the build are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Android Emulator preview APK is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-10d91cef7b50c9c0b41a |
|
iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-3bfd7de4b69899ad6938 |
Spendable (Max and the percentage buttons) subtracted only swapFee, the inclusion fee. On a LI.FI route the swap also pays the route's resourceFee, so Max on an XLM source left too little XLM for the fee. Spendable now uses the same total as the fee display: swapFee plus routes[0].resourceFee for a quote matching the current inputs, via computeTotalFeeXlm. Classic routes and stale quotes keep swapFee alone. The quote-key and total-fee values moved above the spendable memo; they do not depend on it. The amount-error check (useSwapAmountError) stays on the inclusion fee on purpose. A fee-dependent error would clear the quote (path-finding calls clearPath on an amount error), which drops the resource fee, which clears the error, which re-quotes: a loop of LI.FI quotes. Spendable feeds the CTA's insufficient state and the error text, neither of which gates path-finding. Send, the build and the settings input are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Stellar logo The venue is not in /protocols (listing it there would surface it in Discovery), so the route view maps it locally. SwapRouteVenue gains an optional bundled iconSource that SwapVenueIcon prefers over iconUrl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o Figma Figma 3405-175470: 4px gap between token icon and code; path-row venue icons 32px, 4px apart; 16px after the share badge; hop connector is a solid line with a 5px dot, venue icon 24px with a 4px gap; provider chip icons 24px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Figma 3405-175551 uses Blue/04 background, Blue/06 border, Blue/09 14px semibold text; navy-04/06/09 are the nearest SDS tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The route-sheet spacing change also enlarged the Details row stack. Figma 2863-148281 overlaps 16px icons by 4px there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # __tests__/components/screens/SwapScreen/SwapAmountScreen.test.tsx # src/components/screens/SwapScreen/screens/SwapAmountScreen.tsx
…eat/swap-route-screen
There was a problem hiding this comment.
🟡 Changes recommended
Two moderate correctness issues remain: mixed routes can overcount pooled hops, and non-XLM Soroban quotes may pass review without sufficient total XLM fees and fail only during transaction construction.
2 open issues
What changed in this PR
Adds a quote-aware swap Details sheet and expandable Route view, allowing users to inspect network fees, rates, providers, paths, pools, and intermediate tokens for the current quote.
Changes:
- Added localized Details and Route content with expandable paths, provider icons, token icons, shares, and pool information.
- Added Details → Route navigation with centered-sheet back-button support.
- Integrated LI.FI Soroban resource fees into fee display and amount calculations while preserving stale-quote safeguards.
- Added comprehensive unit and component tests for route modeling, fee handling, sheet navigation, and header behavior.
| File | Description |
|---|---|
| src/i18n/locales/pt/translations.json | Adds Portuguese translations for the swap Details and Route views. |
| src/i18n/locales/en/translations.json | Adds English translations for the swap Details and Route views. |
| src/config/types.ts | Adds the optional LI.FI Soroban resource-fee field to swap route types. |
| src/components/screens/SwapScreen/screens/SwapAmountScreen.tsx | Displays quote-aware fee details, integrates the Details sheet, and applies total-fee handling to swap amount controls. |
| src/components/screens/SwapScreen/hooks/useSwapRouteView.ts | Builds the current quote's route view model from swap state and protocol data. |
| src/components/screens/SwapScreen/hooks/index.ts | Exports the swap route view hook. |
| src/components/screens/SwapScreen/helpers/swapRouteView.ts | Maps swap routes into display-ready paths, tokens, venues, shares, and pool information. |
| src/components/screens/SwapScreen/helpers/index.ts | Exports swap route view helpers and types. |
| src/components/screens/SwapScreen/components/SwapVenueIcon.tsx | Renders venue icons, fallback icons, and venue icon stacks. |
| src/components/screens/SwapScreen/components/SwapRouteContent.tsx | Renders expandable route paths with hops, tokens, venues, pools, shares, and providers. |
| src/components/screens/SwapScreen/components/SwapDetailsBottomSheet.tsx | Implements the Details and Route sheet views with fee, rate, provider, and route information. |
| src/components/screens/SwapScreen/components/index.ts | Exports the swap Details sheet component. |
| src/components/BottomSheetHeader.tsx | Adds optional back-button support for centered bottom-sheet headers. |
| tests/components/screens/SwapScreen/SwapAmountScreen.test.tsx | Tests fee presentation, stale-quote gating, Max behavior, and opening the Details sheet. |
| tests/components/screens/SwapScreen/helpers/swapRouteView.test.ts | Tests route transformation, shares, pools, providers, icons, and token labels. |
| tests/components/screens/SwapScreen/components/SwapDetailsBottomSheet.test.tsx | Tests Details-to-Route navigation, route expansion, icons, classic routes, and fee notes. |
| tests/components/BottomSheetHeader.test.tsx | Tests centered-header back-button behavior. |
Review effort: Lite
A path showed its total hop count as "N pools" whenever any hop had a pool, so one pooled hop plus one orderbook hop read "2 pools". The backend's pool is per hop and optional; the count now takes the hops that carry one. Copilot review on #1051 (same line flagged on #3076). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reserve preflight on Review computed the XLM left after fees with the inclusion fee alone. Through LI.FI, a swap into a new trustline token also pays the quote's resource fee, so an account that covers the inclusion fee and the 0.5 XLM reserve but not the resource fee skipped the reserve sheet and failed at submit. It now uses the total the review builds with (the button only enables with a current quote). Copilot review on #3076; the extension gets the same change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce fee The amount screen checked "enough XLM for fees" against the inclusion fee only. A USDC to XLM swap through LI.FI, from an account that covers the inclusion fee but not the quote's resource fee, reached Review and failed at build. The extension already blocks it. The new check compares XLM with the total (inclusion + the current quote's resource fee), disables the CTA and shows the same "insufficient XLM for fees" toast. It deliberately does not set the amount error: that error pauses path-finding, and the resource fee comes from the quote, so gating it on the total would keep a stale fee in place. The test asserts path-finding still sees no amount error, and fails if the total is fed into the amount error instead. Code review, drift review and Copilot on #1051. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gate added in d2d48a8 compared the whole XLM balance with the total fee, so an account whose XLM sits mostly in its minimum balance passed and failed on-chain; it also ran for an XLM source, where spendable already nets the fee out, and could toast after a swap had submitted. It now does what the extension's insufficientXlmForFees does: non-XLM source only, XLM above the minimum balance against the total, and no toast once a transaction hash exists. Medium review on the C4 fix round. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
formatRouteShare rounded half-up before checking for zero, so 0.6% showed "1%". The extension shows "<1%" for anything under 1%; decided to align on that (Piyal, 2026-11-11). A zero or invalid share still hides the pill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The route sheet hid the share pill when a route had one path; the extension always shows it. Decided to align on always showing it (Piyal, 2026-11-11), so single and split routes lay out the same. The sheet test that pinned the old behaviour now expects the pill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hop showed the backend's venue string as sent and only took its icon
from /protocols, so a casing or spacing difference ("sushiswap" against
"SushiSwap") read differently from the extension, which shows the
protocol entry's name. Decided to align on the extension (Piyal,
2026-11-11); an unlisted venue still shows the backend string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s contract as XLM LI.FI relabels only a route's first and last token; middle hops arrive as bare contract IDs. Mobile named them from held balances or the destination and otherwise showed a shortened ID, while the extension also checks the token lists for a code and icon. Decided to align on the extension (Piyal, 2026-11-11): the route view now falls back to the verified token lists, read from the cache the swap picker already fills (readCache, no new request), before the shortened ID. XLM's own contract as a hop showed as a shortened address on both platforms; it now reads XLM with the native token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Route row's provider preview rendered every provider, while the route sheet's path rows and the extension cap at MAX_PATH_VENUE_ICONS with a "+N". Decided to align on the cap (Piyal, 2026-11-11). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decided to use one Portuguese text on both platforms (Piyal, 2026-11-11). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te lands Spendable netted out a Soroban route's resource fee only while a quote for the current amount existed. Max changes the amount, so its re-quote landed with a resource fee and pushed the amount over the limit; the user had to tap Max again. Decided (Piyal, 2026-11-11, option A): the swap duck keeps the last route's resource fee per pair (account, network, from, to), across amount changes and cleared paths, and with no current quote spendable uses it. No fee stored for the pair, or a classic last route, behaves as before. The resource-fee XLM gate still reads the current quote only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three parity gaps the medium review found in the route view, each with its own test (all three failed before): - an empty token-list code fell through as "" with ??; it now reaches the shortened contract ID, as the extension's || does - a classic middle hop got no icon from the token lists (only the destination did); it now takes the listed icon by code and issuer - a duplicate /protocols key let the last entry win; the first wins now, matching the extension's find() One commit: the pre-commit hook runs the full suite, and the three tests share one file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

What
Adds the swap Route screen. A "network fee · See details" line under the receive amount opens a Details sheet with three rows: network fee, rate, and Route ("N providers" with venue icons). The Route row switches the sheet to the Route view, which shows how the swap is routed, from the best route
/swap/quotesreturned:The design follows Figma (Route expand, without amount). It shows no per-hop amounts; no source supplies them yet.
Classic routes use a stand-in until design draws them: the same sheet with "1 path", a 100% pill and no pool count.
The sheet is display only: nothing at quote, build or signing reads it. It shows only the quote for the inputs on screen, so a re-quote in flight never shows the previous route.
Draft, for diff review. Stacked on C3 (#1048). Middle-token names need stellar/freighter-backend-v2#185.
Fee before review matches Send. With stellar/freighter-backend-v2#185, each LI.FI route carries its Soroban resource fee (about 0.085 XLM). The fee shown before review is now the total, inclusion plus resource, with Send's existing "may adjust" note on LI.FI routes. Classic routes are unchanged.
Why
Swap routing spec, client item C4: stellar/wallet-eng-monorepo#131 (open item 2).
Implementation details (for agents)
What changed
BottomSheetHeadergains an optionalonBack(centered layout only; other sheets are unchanged).buildSwapRouteViewanduseSwapRouteViewbuild the view model from the swap store'sswapQuotesroutes[0]. The pool count counts the hops that carry apool. Shares under 1% read "<1%". Providers are the distinct venues, capped at 3 plus "+N" in the Details row. Singular and plural labels are separate keys.SwapAmountScreenrenders the line and one sheet that switches between Details and Route. Tapping See details dismisses the keyboard first.pathResult.quoteKey === currentQuoteKey, the same check as the CTA and the build.truncateAddress; XLM's own contract shows as XLM. Nothing is written to the icon cache.Verification
/code-reviewat high, then medium on the fix diff.Open for design
Known follow-ups
Screenshots
iOS simulator, mainnet, a LI.FI route from a local freighter-backend-v2 (stellar/freighter-backend-v2#178) with LI.FI enabled: the See details line, the Details sheet, the Route view, and the path expanded to its hops (XLM → Aquarius → USDC → SushiSwap → deJTRSY). Nothing was submitted.
🤖 Generated with Claude Code