Distinguish wallet balance from in-market account balance in the trade panel#2147
Conversation
Two pieces of data were being conflated in the trade panel UI:
1. The user's Phantom (or other wallet) USDC balance — what they
have available to deposit.
2. The user's deposited capital on this market's slab account —
what they can actually trade with.
Previously the top of the trade panel showed "ACCOUNT 0 USDC" and
the Size row showed "Bal: 0 USDC" — both reading from the same
in-market capital figure (with a fallback to wallet balance when
no account existed). Users had no way to tell what they had in
their wallet vs what was on-chain in the market without doing
mental math.
Changes:
app/hooks/useWalletAtaBalance.ts (new)
Small hook that fetches the user's associated token account
balance for a given mint via getTokenAccountBalance. Returns
`{ balance, decimals }` or null when the wallet is
disconnected, the mint is null, or the ATA doesn't exist yet.
One-shot fetch on dependency change — no polling, callers can
rebuild on slab capital changes if they want refresh-after-
deposit semantics.
DepositTrigger.tsx — top bar
Renamed "ACCOUNT" to "Wallet Balance:" and switched the value
from `capital` to the wallet's ATA balance via the new hook.
Label and value now share the same brightness (--text), font,
and weight so the whole "Wallet Balance: X USDC" line reads
as one unit. Drops the small uppercase-tracking label that was
at --text-dim and forced users to squint to read which balance
they were looking at.
TradeForm.tsx — Size row
Renamed "Bal:" to "Account Bal:" so the label distinguishes
itself from the wallet-balance line above. Drops the
conditional fallback to walletAtaBalance when no userAccount
exists — pre-account users now see "Account Bal: 0 USDC"
(truthful: they have nothing deposited) rather than the
misleading wallet balance which already shows above. The
in-component walletAtaBalance state is retained for the
margin / CTA logic that still depends on it.
Tests: 2277 app tests passing.
|
@0x-SquidSol is attempting to deploy a commit to the Khubair Nasir's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR adds a new ChangesWallet ATA Balance Tracking
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
app/components/trade/TradeForm.tsx (1)
94-100: ⚖️ Poor tradeoff
TradeFormduplicates the ATA fetch thatuseWalletAtaBalancenow encapsulates
TradeFormhas its own inlineuseEffect(lines 394–415) that callsconnection.getTokenAccountBalancefor exactly the same reason as the new hook — fetching wallet ATA balance and on-chain decimals. This means two separate RPC calls are fired for the same account whenever both components are mounted. MigratingTradeFormtouseWalletAtaBalance(adding decimals to its return value is already done) would remove the duplication.♻️ Sketch of the migration
- const [onChainDecimals, setOnChainDecimals] = useState<number | null>(null); - const decimals = onChainDecimals ?? tokenMeta?.decimals ?? 6; - const [walletAtaBalance, setWalletAtaBalance] = useState<bigint | null>(null); + const { balance: walletAtaBalance, decimals: hookDecimals } = + useWalletAtaBalance(mktConfig?.collateralMint ?? null); + const decimals = hookDecimals ?? tokenMeta?.decimals ?? 6;Then remove the inline
useEffectat lines 394–415.Also applies to: 394-415
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/components/trade/TradeForm.tsx` around lines 94 - 100, TradeForm currently duplicates RPC logic by calling connection.getTokenAccountBalance in its inline useEffect; replace that logic with the new useWalletAtaBalance hook: import and call useWalletAtaBalance(...) and use the returned {balance, decimals} (or equivalent names provided by the hook) instead of maintaining local state variables walletAtaBalance/onChainDecimals and setters (setWalletAtaBalance, setOnChainDecimals); remove the inline useEffect that performs getTokenAccountBalance and any related local state initialization so TradeForm relies solely on the hook-provided values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/hooks/useWalletAtaBalance.ts`:
- Around line 43-50: The current branch checks info.value.amount truthiness and
when it's an empty string (""), the else resets both balance and decimals to
null, discarding valid decimals; update the check in useWalletAtaBalance (the if
using info.value.amount and the setState calls) to only attempt BigInt when
amount is non-empty and not null/undefined (e.g., amount !== "" && amount !=
null) and in the else branch preserve decimals by setting decimals:
info.value.decimals !== undefined ? info.value.decimals : null while leaving
balance null; ensure you reference info.value.amount, info.value.decimals, and
the setState call when making this change.
- Around line 32-60: The hook useWalletAtaBalance currently only fetches once on
changes to publicKey, mint, or connection, causing stale balances after
deposits/withdraws; modify useWalletAtaBalance to accept an optional
refreshTrigger (e.g., a number or boolean) parameter and include it in the
useEffect dependency array so callers can toggle refreshTrigger to force a
re-fetch; ensure the effect still computes ata via getAssociatedTokenAddressSync
and calls connection.getTokenAccountBalance, preserves the cancelled flag logic,
and updates setState the same way when the refreshTrigger changes.
---
Nitpick comments:
In `@app/components/trade/TradeForm.tsx`:
- Around line 94-100: TradeForm currently duplicates RPC logic by calling
connection.getTokenAccountBalance in its inline useEffect; replace that logic
with the new useWalletAtaBalance hook: import and call useWalletAtaBalance(...)
and use the returned {balance, decimals} (or equivalent names provided by the
hook) instead of maintaining local state variables
walletAtaBalance/onChainDecimals and setters (setWalletAtaBalance,
setOnChainDecimals); remove the inline useEffect that performs
getTokenAccountBalance and any related local state initialization so TradeForm
relies solely on the hook-provided values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0c1fd79e-8b65-45c0-998e-8438a83a4197
📒 Files selected for processing (3)
app/components/trade/DepositTrigger.tsxapp/components/trade/TradeForm.tsxapp/hooks/useWalletAtaBalance.ts
…cimals on empty-amount edge case Both findings from the CodeRabbit pass on PR dcccrypto#2147. 1. (Major) Wallet balance went stale after every deposit / withdraw. useWalletAtaBalance only re-fetched on publicKey / mint / connection changes — none of which fire when the user deposits via the inline DepositWithdrawCard. So a deposit decreased the on-chain ATA balance but the bar kept showing the pre-deposit number until the page reloaded. Fix: hook now accepts an optional `refreshTrigger` arg and includes it in the effect's deps. DepositTrigger passes the in-market `capital` value as the trigger — capital changes on every settled deposit / withdraw / trade-fee event, which is exactly when the wallet ATA balance also changes on-chain. Capital also changes on trade settlement (fees), which causes an unnecessary re-fetch, but ATA fetches are cheap single RPC calls and the simpler "any capital change = refresh" trigger beats the alternative of plumbing explicit deposit/withdraw event signals through the component tree. 2. (Minor) The else branch in the success path of the fetch reset both balance AND decimals to null when info.value.amount was empty (an unusual malformed-RPC case), discarding any usable info.value.decimals from the same response. Now we compute on-chain decimals once at the top of the success block and preserve them across both branches. Tests: 11 TradeForm tests passing.
Summary
Two pieces of data were being conflated in the trade panel UI:
Previously the top of the trade panel showed "ACCOUNT 0 USDC" and the Size row showed "Bal: 0 USDC" — both reading from the same in-market capital figure (with a fallback to wallet balance when no account existed). Users had no way to tell what they had in their wallet vs what was on-chain in the market.
Changes
New hook `useWalletAtaBalance` fetches the user's associated token account balance via `getTokenAccountBalance`. One-shot fetch on dependency change — no polling.
Why
The relabel makes the relationship between the two figures explicit:
Pre-account users now see `Account Bal: 0 USDC` (truthful — they haven't deposited anything yet) instead of the previous fallback to wallet balance, which was misleading because the same number now appears above as Wallet Balance.
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
Improvements
Bug Fixes