Skip to content

fix(frontend): load the OISY Trade balances during initialization - #13998

Merged
sbpublic merged 14 commits into
mainfrom
impr/frontend/load-oisy-trade-at-init
Sep 11, 2026
Merged

fix(frontend): load the OISY Trade balances during initialization#13998
sbpublic merged 14 commits into
mainfrom
impr/frontend/load-oisy-trade-at-init

Conversation

@sbpublic

@sbpublic sbpublic commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Motivation

The hero net worth already counts DEX-deposited balances — ExchangeBalance adds $oisyTradeUsdValue to the total — but loadOisyTrade was only ever called from TradingList (the Trading tab) and OisyTradeProvider (the OISY Trade page), both through an IntervalLoader. Until one of those surfaces was opened, oisyTradeStore stayed empty, so a freshly logged-in user saw a total that silently excluded everything they had deposited on the DEX.

The Earn data does not have this problem: Liquidium loads app-wide through LoaderLiquidium in the Loaders tree, in addition to its page-level loader. This PR gives the trading data the same treatment.

Changes

  • Add LoaderOisyTrade, an app-wide loader that loads the DEX balances reactively on the identity — a direct mirror of LoaderLiquidium.
  • Mount it in Loaders next to LoaderLiquidium, so the deposited balances are fetched during initialization.
  • Guard the oisyTradeStore commit in loadOisyTrade on the loading identity still being the current one, so a request left in flight at sign-out cannot write the previous account's balances back after the reset (review item).
  • Gate the loader on OISY_TRADE_ENABLED rather than the anyTradingProviderEnabled aggregate, so it stays off when the provider is even if another one keeps the Trading surface reachable (review item).
  • Take a monotonic loadGeneration at the top of loadOisyTrade and let only the newest invocation commit, so an action-triggered refresh cannot be overtaken by a poll for the same account that was already in flight (review item).
  • Narrow the app-wide load to loadOisyTradeBalances, which fetches getBalances alone and commits through a new setBalances partial writer, so initialization no longer pulls pairs, supported tokens and the order history that only the Trading surfaces read (review item).

The page-level IntervalLoaders stay as they are: they keep the Trading tab and the provider page polling while visible, exactly as LiquidiumProvider does alongside LoaderLiquidium.

Side effect: the Trading tab no longer flashes its skeleton on first visit, since oisyTradeLoaded is already true by the time it mounts.

Tests

  • New LoaderOisyTrade.spec.ts: loads with an identity, still calls the loader when signed out (so the store resets), and does nothing when the provider is disabled.
  • New cases in oisy-trade.services.spec.ts for the identity guard: an older load resolving after a sign-out, and after a second account's load — both verified to fail without the guard.
  • LoaderOisyTrade.spec.ts now drives anyTradingProviderEnabled and OISY_TRADE_ENABLED independently, covering the mixed-provider configuration; both gate cases verified to fail against the old aggregate guard.
  • A further case for two same-identity loads with the older resolving last, also verified to fail without the generation check.
  • Cases for the balances-only path: it issues just the one query, leaves pairs, tokens and orders untouched over a prior full load, resets on a nullish identity, drops a result whose identity changed in flight, and swallows canister errors — the partial-writer cases verified to fail against a full set.
  • npm run check and npm run check:tests report no errors in the touched files.

Not verified in a browser: reproducing it needs a logged-in session with an actual OISY Trade deposit.


🤖 Generated with Claude Code using Claude Opus 5 (claude-opus-5)

The hero net worth counts DEX-deposited balances via `oisyTradeUsdValue`,
but `loadOisyTrade` only ran from the Trading tab and the OISY Trade page,
so a fresh login showed a total that excluded them until one of those
surfaces was visited.

Add `LoaderOisyTrade` to the app-wide `Loaders` tree, mirroring
`LoaderLiquidium` (the Earn data), so pairs, supported tokens, balances
and orders load during initialization.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sbpublic
sbpublic requested a balanced review from Copilot September 9, 2026 10:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Identity-transition races can expose stale account data, and the loader uses the aggregate rather than provider-specific feature gate.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds app-wide OISY Trade loading so deposited balances are included in net worth immediately after initialization.

Changes:

  • Adds a reactive OISY Trade loader.
  • Mounts it in the global loader tree.
  • Adds loader behavior tests.
File summaries
File Description
LoaderOisyTrade.svelte Loads OISY Trade data app-wide.
Loaders.svelte Mounts the new loader.
LoaderOisyTrade.spec.ts Tests identity and feature-gate behavior.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/frontend/src/lib/components/loaders/LoaderOisyTrade.svelte Outdated
Comment thread src/frontend/src/lib/components/loaders/LoaderOisyTrade.svelte Outdated
sbpublic and others added 3 commits September 9, 2026 13:10
…rrent

`loadOisyTrade` captured the identity at call time, awaited four canister
calls and then committed to `oisyTradeStore` unconditionally. Making the
load app-wide put that on the sign-out path: `LoaderOisyTrade` re-runs its
effect with a nullish identity and resets the store, while the in-flight
load for the previous account can still resolve afterwards and write its
balances back — so the hero kept summing them until the reload committed.

Re-read `authIdentity` at the commit point and drop the result when the
principal no longer matches. Guarding in the service rather than the
loader covers the deposit, withdraw and limit-order reload paths too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`LoaderOisyTrade` guarded on `anyTradingProviderEnabled`, the surface-level
aggregate, while everything it does is provider-specific — `loadOisyTrade`
talks to the oisy_trade canister. The two are the same value today, but the
codebase models the Trading surface staying reachable through another
provider with OISY TRADE off, and in that configuration the loader would
call the disabled provider's APIs on every initialization.

Gate on `OISY_TRADE_ENABLED` instead, matching the split `TradingList` and
`OisyTradeProvider` already make, and drive both flags independently in the
spec so the mixed-provider configuration is covered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Concurrent loads for the same identity can still commit stale balances and orders out of sequence.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/frontend/src/lib/services/oisy-trade.services.ts Outdated
sbpublic and others added 5 commits September 9, 2026 15:41
The identity check compares principals only, so two loads for the same
account were indistinguishable at the commit and the last one to resolve
won regardless of which started first. `loadOisyTrade` has several
concurrent callers — the app-wide loader, the initial load each
`IntervalLoader` fires on mount, the poll, and the post-deposit /
post-withdraw / post-limit-order refreshes — so a refresh triggered by an
action could be overtaken by a poll that was already in flight and put the
pre-action balance back on screen.

Take a monotonic `loadGeneration` at entry, before the nullish-identity
branch so a sign-out invalidates whatever is in flight too, and commit only
when that generation is still the latest and the identity still current.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The app-wide loader unnecessarily fetches up to 500 orders, increasing canister traffic and delaying the balance needed by the hero.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/frontend/src/lib/components/loaders/LoaderOisyTrade.svelte Outdated
sbpublic and others added 2 commits September 11, 2026 09:48
The app-wide loader called the full `loadOisyTrade`, which fetches pairs,
supported tokens, balances and up to five pages of order history and
commits all four at once. The hero reads none of that except the balances
— `oisyTradeUsdValue` derives from `oisyTradeBalances` joined against
`enabledIcTokens` and `exchanges` — so every signed-in initialization made
three canister queries whose results are only ever read on the Trading
surfaces, and deep order histories delayed the total.

Add `loadOisyTradeBalances`, fetching `getBalances` alone and committing
through a new `setBalances` partial writer so it cannot blank what the
Trading tab loaded. Mirrors the existing `loadOisyTradeSwapPairs`/
`setPairs` carve-out for the quote path. The page-level loaders keep the
full `loadOisyTrade`, so orders stay page-level.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sbpublic
sbpublic marked this pull request as ready for review September 11, 2026 07:49
@sbpublic
sbpublic requested a review from a team as a code owner September 11, 2026 07:49
@zeropath-ai

zeropath-ai Bot commented Sep 11, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 47fe7bd.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► src/frontend/src/lib/components/loaders/LoaderOisyTrade.svelte
    Add app-wide OISY Trade loader component with identity-aware loading
Enhancement ► src/frontend/src/lib/components/loaders/Loaders.svelte
    Register LoaderOisyTrade in Loader collection
Enhancement ► src/frontend/src/lib/services/oisy-trade.services.ts
    Add loadOisyTradeBalances function for balances-only loading
    Use identity in balances loading
    Guard writes with load generation and identity checks
    Integrate with isCurrentLoad logic
Enhancement ► src/frontend/src/lib/stores/oisy-trade.store.ts
    Extend store interface with setBalances method
    Preserve existing state when updating balances
Test/Spec ► src/frontend/src/tests/lib/components/loaders/LoaderOisyTrade.spec.ts
    Add tests for LoaderOisyTrade behavior under various provider/identity conditions
Test/Spec ► src/frontend/src/tests/lib/services/oisy-trade.services.spec.ts
    Extend tests to cover loadOisyTrade and loadOisyTradeBalances interactions
    Include identity-transition scenarios and balance-only loading behavior

@sbpublic
sbpublic added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit d738590 Sep 11, 2026
80 checks passed
@sbpublic
sbpublic deleted the impr/frontend/load-oisy-trade-at-init branch September 11, 2026 12:02
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.

3 participants