Problem
The Swap tab hardcodes its asset universe — SWAP_ASSETS (7 classic broker IDs) + BROKER_TO_CONTRACT (4 Soroban contracts) in frontend/src/views/swap.ts. Asset selection is two plain Select dropdowns: no search, no logos. Slippage is limited to three fixed chips (0.1 / 0.5 / 1.0%). Users can't reach most tradable assets, can't search by contract id, and can't set a custom slippage.
Goal
- Replace the hardcoded list with the LOBSTR curated assets feed:
https://lobstr.co/api/v1/sep/assets/curated.json (CORS *, ~51 assets, 14 KB).
- Let users browse the list or search by contract id to pick sell/buy assets.
- Show token logos from the feed (
icon).
- Add a 4th slippage option: a custom numeric input (e.g. 2.4, 10, 0.4).
Verified facts
- Feed is browser-fetchable (
Access-Control-Allow-Origin: *); network: "public".
- Each asset:
{ contract (64-hex), code, issuer (G…), name, domain, icon (url), decimals }.
StrKey.encodeContract(Buffer.from(contract,"hex")) reproduces the real C… id (USDC hex → CCW67TSZ…, EURC → CDTKPWPL…, both match today's hardcoded contracts).
- XLM is not in the feed → inject a synthetic native entry (brokerId
XLM, contract CAS3…).
Plan
1. Data layer — frontend/src/views/swapAssets.ts (new)
- Fetch + cache the curated feed (in-memory + sessionStorage; refetch when stale).
- Map each entry →
{ symbol, name, domain, icon, decimals, issuer, brokerId: "CODE-ISSUER", contractId: StrKey(hex) }.
- Prepend synthetic XLM.
- Fallback to the current hardcoded 7 assets if the fetch fails (no dead screen).
2. Searchable asset picker — replaces the two Selects
- Sell/buy pills (logo + code) open a search modal: text box + scrollable list (logo, code, name, domain).
- Filter by code / name / domain and contract id — accept both
C… StrKey and raw hex.
- Decision: curated-only. A pasted
C… not in the feed shows "not in curated list" (no arbitrary-contract path).
- Logos via
<img loading="lazy"> with a letter-avatar fallback on load error.
3. Custom slippage
- Keep 0.1 / 0.5 / 1.0 chips; add a 4th Custom chip → numeric input feeding the same
slip().
- Decision: accept 0 < x ≤ 50; show a high-slippage warning above 5%. Reject ≤0 / >50 / non-numeric.
4. Wiring
brokerId → estimateSwap; contractId → fetchAssetBalance + aquariusBestRate.
- Remove
SWAP_ASSETS / BROKER_TO_CONTRACT.
- Execute path unchanged (still gated
SWAP_EXECUTE_ENABLED = false).
Files
frontend/src/views/swapAssets.ts (new)
frontend/src/views/swap.ts
frontend/src/views/swap.css
Verification
npm run build + npm run lint clean.
- Live curated fetch + hex→StrKey parity check.
- Playwright e2e boot suite green (no TDZ/null regression).
- Manual: browse + search-by-contract-id (C… and hex), pick across pairs, logos render/fallback, custom slippage accept/reject + >5% warning, quote + DEX-rate cross-check still work.
Out of scope
- Swap execution (broker trade-XDR builder) — remains "coming soon".
- Arbitrary non-curated contracts.
Problem
The Swap tab hardcodes its asset universe —
SWAP_ASSETS(7 classic broker IDs) +BROKER_TO_CONTRACT(4 Soroban contracts) infrontend/src/views/swap.ts. Asset selection is two plainSelectdropdowns: no search, no logos. Slippage is limited to three fixed chips (0.1 / 0.5 / 1.0%). Users can't reach most tradable assets, can't search by contract id, and can't set a custom slippage.Goal
https://lobstr.co/api/v1/sep/assets/curated.json(CORS*, ~51 assets, 14 KB).icon).Verified facts
Access-Control-Allow-Origin: *);network: "public".{ contract (64-hex), code, issuer (G…), name, domain, icon (url), decimals }.StrKey.encodeContract(Buffer.from(contract,"hex"))reproduces the realC…id (USDC hex →CCW67TSZ…, EURC →CDTKPWPL…, both match today's hardcoded contracts).XLM, contractCAS3…).Plan
1. Data layer —
frontend/src/views/swapAssets.ts(new){ symbol, name, domain, icon, decimals, issuer, brokerId: "CODE-ISSUER", contractId: StrKey(hex) }.2. Searchable asset picker — replaces the two
SelectsC…StrKey and raw hex.C…not in the feed shows "not in curated list" (no arbitrary-contract path).<img loading="lazy">with a letter-avatar fallback on load error.3. Custom slippage
slip().4. Wiring
brokerId→estimateSwap;contractId→fetchAssetBalance+aquariusBestRate.SWAP_ASSETS/BROKER_TO_CONTRACT.SWAP_EXECUTE_ENABLED = false).Files
frontend/src/views/swapAssets.ts(new)frontend/src/views/swap.tsfrontend/src/views/swap.cssVerification
npm run build+npm run lintclean.Out of scope