fix(app): resolve the token program before reading a balance - #21
Merged
Merged
Conversation
Swapping USDC for a Token-2022 xStock succeeded and then showed a zero balance for the token the wallet was now holding, so the percentage shortcuts stayed disabled and it could neither be swapped back nor added as liquidity. Nothing was lost on-chain: the mint is owned by TokenzQdBN… and the tokens are in the Token-2022 ATA, which is simply not the address the UI was looking at. An associated-token address is seeded with the token program, so deriving a Token-2022 ATA under Tokenkeg yields an address that does not exist. On a transaction that fails loudly at simulation. On a balance read it throws, the caller catches, and the UI renders 0 — indistinguishable from an empty wallet. Four read sites had it, and the pattern was identical in every one: the *transaction* next to it had been migrated to Token-2022 and resolves both programs properly, while the *balance read* kept `userAta`'s classic-SPL default. - AmmSwap: the swap card's input balance (the reported symptom). - Pools: the add-liquidity balances for either pool side. - PartnerStream and Gauge: the bribe-token balance. `reward_mint` is arbitrary by design — a partner bribing in USDG never touches the AMM — which is the same surface the program-side migration had to be reminded of. `userAtaAuto` now pairs the existing memoised `getMintProgram` with the ATA derivation, so a caller holding a mint from a picker, a pool or a partner record cannot silently get the wrong address. `userAta` keeps its default for the mints we control — SOLA, oSOLA, USDC and LP mints are deliberately classic SPL — and carries a ☢️ pointing at the other helper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The symptom
Swapping USDC for a Token-2022 xStock on devnet succeeded — USDC debited, NVDAx in the wallet — and the swap card then showed a zero balance for the token the wallet was now holding. The percentage shortcuts are gated on that balance (
disabled={!balanceIn}), so the token could neither be swapped back nor added as liquidity.Nothing was ever at risk on-chain. The NVDAx mint
Fno1yG9awfotJwo4XJ1wE6c6vzRXPo3TERrmcQkMBGfois owned byTokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb(verified against devnet), and the tokens sit in the Token-2022 ATA. That address is simply not the one the UI was looking at.The cause
An associated-token address is seeded with the token program. Deriving a Token-2022 ATA under Tokenkeg yields an address that does not exist. On a transaction that fails loudly at simulation; on a balance read it throws, the caller catches, and the UI renders
0— indistinguishable from an empty wallet.Four sites, one pattern
In every one, the transaction beside it had already been migrated to Token-2022 and resolves both programs correctly, while the balance read kept
userAta's classic-SPL default:AmmSwap.tsxPools.tsxPartnerStream.tsxGauge.tsxThe last two matter beyond the xStocks:
reward_mintis arbitrary by design — a partner bribing in USDG never touches the AMM — which is the same surface the program-side Token-2022 migration had to be reminded of.The fix
userAtaAutopairs the existing memoisedgetMintProgramwith the ATA derivation, so a caller holding a mint from a picker, a pool or a partner record cannot silently get the wrong address.userAtakeeps its default for the mints we control (SOLA, oSOLA, USDC and LP mints are deliberately classic SPL) and now carries a ☢️ pointing at the other helper.Verification
yarn buildclean — type-check included, which is what caught a bad import on the first pass.🤖 Generated with Claude Code