Skip to content

fix(wallets): resolve drained IBEX accounts as zero balance (admin API broken for migrated accounts)#441

Merged
islandbitcoin merged 1 commit into
mainfrom
fix/drained-wallet-balance-zero
Jul 10, 2026
Merged

fix(wallets): resolve drained IBEX accounts as zero balance (admin API broken for migrated accounts)#441
islandbitcoin merged 1 commit into
mainfrom
fix/drained-wallet-balance-zero

Conversation

@islandbitcoin

Copy link
Copy Markdown
Contributor

Bug

Since the USDT cutover completed (2026-07-10), the admin GraphQL API fails for every migrated account: accountDetailsByUsername/ByAccountIdwallets[].balance on the legacy (drained) USD wallet throws IBEX_ERROR, poisoning the whole response. Any admin-API consumer (Frappe Account Hub) breaks on ~all customer lookups.

Why admin only: the cash-wallet compat redirect in the USD wallet resolver only runs when cashWalletClientCapabilities/domainAccount are in ctx — true for app users on the public API (compat verified during cutover), never for admin ctx → raw read of the drained wallet.

Root cause: IBEX omits the balance field for drained/never-funded accounts (absent = zero; verified in prod during the cutover). getBalanceForWallet has a ZERO arm for httpCode 404 but treated the missing-field case as UnexpectedIbexResponse("Balance not found") → resolver throws.

Evidence

Direct prod admin-API query (patoo, migrated, $20):

{"errors":[{"path":["accountDetailsByUsername","wallets",0,"balance"],"code":"IBEX_ERROR"}],
 "data":{"accountDetailsByUsername":{"wallets":[
   {"walletCurrency":"USD","balance":null},
   {"walletCurrency":"BTC","balance":null},
   {"walletCurrency":"USDT","balance":2000}]}}}

Same signature on TEST for every migrated account (ben4, baa).

Fix

Missing balance on an otherwise-valid account-details response resolves to the currency's ZERO — consistent with the 404 arm and the cutover verifier's drained-reads-as-zero semantics. Applies to all consumers; no resolver/ctx changes.

Tests

TEST=get-balance-for-wallet yarn test:unit → 3 passed (existing + drained-USD + drained-USDT). tsc clean.

Deploy note

Wants to ride the next chart release — Account Hub (Frappe) stays broken for migrated accounts until this lands in prod.

🤖 Generated with Claude Code

IBEX omits the balance field for drained / never-funded accounts (absent
means zero — verified in prod during the USDT cutover). getBalanceForWallet
treated that as UnexpectedIbexResponse('Balance not found'), which throws in
the wallet balance resolvers.

Post-cutover this broke the ADMIN GraphQL API for every migrated account:
wallets[].balance on the legacy (drained) USD wallet errors with IBEX_ERROR,
failing the whole accountDetails* response. The cash-wallet compat redirect
does not apply in admin ctx (no client capabilities / domainAccount), so the
admin path always does the raw read. Public app API was unaffected.

Evidence (prod, patoo): wallets -> USD balance null + IBEX_ERROR, USDT 2000
reads fine. Same signature on TEST for any migrated account.

Fix: missing balance field on an otherwise-valid response resolves to the
currency's ZERO, matching the existing 404 arm and cutover-verifier
semantics. Unit tests for both currencies.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@islandbitcoin islandbitcoin merged commit d719d9e into main Jul 10, 2026
15 checks passed
@linear

linear Bot commented Jul 10, 2026

Copy link
Copy Markdown

ENG-490

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.

2 participants