Skip to content

feat(swap): reconciliation ceiling + sub-denom pricing (+ worktree catch-up)#378

Merged
BitHighlander merged 2 commits into
developfrom
fix/swap-subdenom-price
Jul 24, 2026
Merged

feat(swap): reconciliation ceiling + sub-denom pricing (+ worktree catch-up)#378
BitHighlander merged 2 commits into
developfrom
fix/swap-subdenom-price

Conversation

@BitHighlander

Copy link
Copy Markdown
Collaborator

Catches develop up with the in-progress swap/reconciliation worktree and folds in two targeted bug fixes. The whole worktree builds clean (vite 2356 modules + bun bundle 7.61 MB).

Fix 1 — Sub-denom swap pricing (SwapDialog.tsx)

A THORChain/Cosmos sub-denom (TCY, RUJI, xRUJI, secured assets) shares its chain's single ChainBalance, whose native price is the gas asset (RUNE). The price resolver treated any non-EVM-token asset as native, so receiving 102.83 TCY was valued at RUNE's ~$0.42 = $43.20 instead of TCY's $0.12 = **$12** — a $12 input looked like a $43 output.

fromPriceUsd/toPriceUsdFromBalance now apply the native price only when the ChainBalance actually represents the asset (symbol match, via balanceIsForAsset); otherwise return 0 so toPriceUsd derives from the quote ratio × the correct from-price. Native swaps (BTC/ETH/RUNE→X) and EVM tokens are unchanged.

Fix 2 — Post-swap reconciliation ceiling (balance-reconciliation.ts, Dashboard.tsx)

A terminal-but-unconfirmed swap protected the last snapshot forever if the destination indexer never reported the increase — e.g. a RUNE→ZEC transparent receive where ZEC rides a single NowNodes node (no failover) that lags/403s. The banner had no ceiling and no dismiss, so it spun indefinitely.

After RECONCILIATION_CEILING_MS (15 min) the record expires: protectedBalanceChainIds releases it so the real balance surfaces, and the gold spinner banner flips to a dismissible "Couldn't confirm your {symbol} balance yet — check the explorer" notice. Observed (confirmed) swaps are unaffected.

Follow-up (Pioneer, separate repo): ZEC transparent balance is a single-node dependency with no failover — worth a second source or a degraded flag so the vault can tell "still syncing" from "source down."

Also included (worktree catch-up)

Offline asset icons (vendor-asset-icons script + offlineAssetIcons.json + vendored token-icon PNGs), custom-slippage UI, Solana opaque-signing gate, swap parsing, and the balance-reconciliation feature these fixes build on — each with tests. Excludes modules/ submodule bumps and keepkey-sdk build artifacts.

Verification

  • bunx vite build — 2356 modules, clean
  • bun scripts/bundle-backend.ts — 7.61 MB, clean
  • assert checks: sub-denom pricing yields ~$12 (native BTC unaffected); ceiling expires past-window + releases protection, observed unaffected

🤖 Generated with Claude Code

…catch-up

Catches develop up with the in-progress swap/reconciliation worktree
(offline asset icons, custom slippage, Solana opaque-signing, balance
reconciliation, swap parsing) and folds in two targeted bug fixes:

1. Sub-denom swap pricing (SwapDialog.tsx)
   A THORChain/Cosmos sub-denom (TCY, RUJI, xRUJI, secured assets) shares
   its chain's single ChainBalance, whose native price is the gas asset
   (RUNE). The resolver treated any non-EVM-token asset as native, valuing
   102.83 TCY at RUNE's ~$0.42 = $43.20 instead of TCY's ~$0.12 = ~$12.
   from/toPriceUsd now apply the native price only when the ChainBalance
   actually represents the asset (symbol match); otherwise 0, so toPriceUsd
   derives from the quote ratio and shows the honest ~$12.

2. Post-swap reconciliation ceiling (balance-reconciliation.ts, Dashboard)
   A terminal-but-unconfirmed swap protected the last snapshot forever if
   the destination indexer never reported the increase — e.g. ZEC rides one
   NowNodes node with no failover. After RECONCILIATION_CEILING_MS (15m) the
   record expires: protection releases so the real balance surfaces, and the
   spinner banner flips to a dismissible "couldn't confirm — check explorer"
   notice instead of spinning indefinitely.

Verified: bunx vite build (2356 modules) + bun bundle (7.61 MB) both clean;
assert checks for both fixes pass. Excludes modules/ submodule bumps and
keepkey-sdk build artifacts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@BitHighlander
BitHighlander merged commit 1ca9df0 into develop Jul 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant