Swap stays on "Calculating..." and never returns a quote #484
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#484
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Swap stays on "Calculating..." and never returns a quote
What broke: The Swap page never calculates the output amount. It remains stuck on "Calculating..." indefinitely.
Steps to reproduce:
Expected behavior: The DEX should calculate and display the swap quote within a few seconds.
Actual behavior: The interface stays on "Calculating..." forever and no quote is returned. The swap button also remains in the "CALCULATING..." state.
Environment:
Device: Android
Wallet: Keplr Mobile
Browser: Keplr in-app browser
Screenshot:
changed the description
Requires investigation before implmenetation - to investigator agent, please provide relevant files, code, possible reasons, verification criteria, and an implementation plan
Investigation findings (#484)
User report (Keplr Android in-app browser): JADE → RUBY with amount
22.5stays on Calculating... (receive field + CTA) indefinitely.What “Calculating...” actually means
Both surfaces are driven by the React Query sim query on Swap:
simQuery.isLoading || simQuoteStaleSwapPage.tsx(~919–921)simQuery.isFetchingSwapPage.tsx(~1324–1327)simQuoteStale(isSubmitQuoteStaleinquoteDebounce.ts) is true wheneverisFetchingis true (also debounce / placeholder / hybrid snapshot mismatch). So any long or overlapping fetch keeps both the button and the output on Calculating.There is no wallet gate on the quote itself (
enabled: hasRoute && positive amount), but a connected wallet does passtrader=into indexer + LCD sims (#245).JADE → RUBY is not a direct pair
Mainnet soft-launch has no JADE/RUBY pair. Tokens are CW20s:
terra1ejq3mjjgnklpa3pg4jterlfwsny055gpmcjf3fz0ev3ueajnzeysz6xxgrterra1fga508hzx8dd7x8q4uhm6mdhkqv6fxrtsea3r27smdqmv5k2jgxq5zk9fcQuote path is the CW20 multi-hop branch in
SwapPage.tsx(~479–527):GET /api/v1/route/solve(getRouteSolve, 15s client timeout + 1 retry)enrichSwapOperationsWithHopMinReturns— ~2 LCD calls per hopsimulateMultiHopSwap— router LCDpreflightSwapRouteSpread— another ~2 LCD calls per hopdirectHybridQuote.tsis not on this path (direct-pair only).Production probe (this investigation)
Result just now:
router_operationsestimated_amount_out:8339(~0.008339 RUBY for 22.5 JADE)lcd_hybrid_queries: 357intermediate_tokenslength 5)Frontend indexer client timeout is
FETCH_TIMEOUT_MS = 15_000(indexer/client.ts). A ~24s solve exceeds the client abort, so the browser often times out, falls through to client BFS + more LCD work, while React Query still hasrefetchInterval: 10_000onsimQuery(SwapPage.tsx:581) and thequeryFndoes not take an AbortSignal for cancellation of superseded runs.Likely root cause (ranked)
Highest — refetch pile-up on a slow multi-hop quote
JADE→RUBY quote work routinely exceeds 10–15s (indexer alone measured ~24s). With
refetchInterval: 10_000and no query cancellation, overlapping fetches keepisFetching === truealmost continuously → perpetual Calculating on button + receive. Keplr mobile WebView makes sequential LCD fan-out worse.High — receive UI hides prior quote while refetching
Output uses
simQuery.isFetchingeven whenkeepPreviousDatastill has a priorsimData, so every background refetch replaces the amount with Calculating.Medium — 15s indexer timeout vs slow
global_v1for this pairTimeout → catch → client BFS fallback doubles work; React Query retries (App.tsx) extend “Calculating” further before “Quote unavailable”.
Medium — Keplr in-app browser amplification
Same code path on desktop may eventually settle; mobile WebView + public LCD connection limits amplify hangs. Not a Keplr signing bug (quote is off-chain HTTP).
Lower — debounce / hybrid settings flicker
Unlikely for a stable
22.5input unless IME/settings keep changing the sim key.Not suspected as primary: amount parsing (
22.5→22500000at 6 decimals), missing CW20 eligibility, or CSP/CORS (those fail fast).Relevant files
frontend-dapp/src/pages/SwapPage.tsx— sim query, Calculating gates, CW20 multi-hop orchestrationfrontend-dapp/src/hooks/useSubmitAlignedSimQuote.ts/frontend-dapp/src/utils/quoteDebounce.ts—simQuoteStale/isFetchingfrontend-dapp/src/services/indexer/client.ts—getRouteSolve, 15s timeoutfrontend-dapp/src/services/terraclassic/swapRoutePreflight.ts— per-hop LCD enrich/preflightfrontend-dapp/src/services/terraclassic/router.ts/queries.ts— multi-hop sim + 10s LCD timeoutfrontend-dapp/src/components/trade/TradeMarketOrderPanel.tsx— samerefetchInterval: 10_000patternindexerroute solver (route_solver) — hybridglobal_v1cost for distant pairsdeployments/mainnet-soft-launch/deploy-trace.md— pair graph (no direct JADE/RUBY)Verification checklist
22.5, wallet connected; note whether Calculating exceeds ~2 minutes vs eventual Quote unavailablechrome://inspect): confirm overlappingGET …/api/v1/route/solveevery ~10s while prior request still pending; count LCD/smart/burstslcd_hybrid_queriestraderand retest — isolates fee-tier/indexer cache path (#245)Suggested fix plan (implementation order)
simQuery.queryFn(indexer / enrich / router / preflight) and confirm refetch overlap on mobile.refetchIntervalwhilefetchStatus === 'fetching'(or raise multi-hop interval).signalthrough indexer + LCD fetches so superseded quotes abort.getPairper quote; avoid duplicate enrich+preflight sims where safe).Follow-ups (only if relevant after quote works)
ROUTER_SWAP_OPS_MIN_GAS_PER_HOP/ known EMBER→JADE→RUBY OOG notes inconstants.ts) — separate from the hang, same token family.estimated_amount_outfor 22.5 JADE may warrant a product check on liquidity/route quality once quoting settles.Bottom line: Most consistent with a UI/refetch state machine issue layered on a legitimately expensive JADE→RUBY multi-hop quote (prod indexer ~24s, 4 hops, 357 LCD hybrid queries), amplified on Keplr mobile — not a missing route or amount bug.
mentioned in commit
6b214dd161mentioned in merge request !1020
mentioned in commit
6873d06cd5mentioned in issue #485
marked as related to #485
mentioned in commit
24594c5f4cmentioned in merge request !1021
mentioned in issue #496
mentioned in commit
70937b9c1fmentioned in merge request !1031
mentioned in issue #595
mentioned in issue #694