Skip to content

feat(sdk-core): pre-flight balance check for depositToVault#9293

Open
bitgo-ai-agent-dev[bot] wants to merge 1 commit into
masterfrom
DEFI-371-pre-flight-balance-check-deposit
Open

feat(sdk-core): pre-flight balance check for depositToVault#9293
bitgo-ai-agent-dev[bot] wants to merge 1 commit into
masterfrom
DEFI-371-pre-flight-balance-check-deposit

Conversation

@bitgo-ai-agent-dev

Copy link
Copy Markdown

Summary

  • Adds InsufficientBalanceError (structured error with assetToken, available, requested fields) to defiVault.ts and exports it from the defi module index.
  • Adds assertSufficientAssetBalance private method that fetches a fresh wallet snapshot (?allTokens=true) and compares the spendable balance of the vault's assetToken against params.amount using BigNumber.
  • depositToVault calls this check after fetching vault config, before dispatching to depositToConcreteVault or depositToMorphoVault.

Decision log

1. Where to place the balance check

  • defiVault.ts:102depositToVault is the single entrypoint for all deposit paths.
  • Options: (A) In depositToVault before dispatch — covers both Concrete and Morpho uniformly; (B) Duplicated in each private path; (C) Caller's responsibility.
  • Chose A: single check, DRY, applies to all paths.

2. Balance source: stale _wallet vs. fresh API call

  • _wallet.spendableBalanceString may be stale (wallet constructed before deposit call).
  • Options: (A) Fetch fresh via GET /wallet/{id}?allTokens=true; (B) Use cached _wallet data.
  • Chose A: fresh fetch guarantees correctness. ?allTokens=true ensures ERC-20 token balances are included.

3. Token lookup: assetToken === walletCoin vs. tokens array

  • For Concrete BTC: vault assetToken is 'btc' which matches vault coin 'btc' — use top-level spendableBalanceString.
  • For Morpho: vault assetToken is e.g. 'usdc', vault coin is 'eth' — search the tokens array by tokenName.
  • Missing token entry → balance treated as '0' → throws InsufficientBalanceError.

4. BigNumber for comparison

  • Used BigNumber (already a dep of sdk-core) to avoid float precision issues on base-unit integer strings. Never floats for monetary amounts.

5. Test structure

  • depositToVault now makes two GET requests: vault config + wallet balance.
  • Updated all existing tests to use onFirstCall()/onSecondCall() on mockBitGo.get.
  • Fixed mockBaseCoin.url stub to return a real URL so wallet.url() works.

Test evidence

New tests added (5 total):

  • concrete_btccx: throws InsufficientBalanceError when native coin balance is too low
  • concrete_btccx: does not throw when balance exactly equals requested amount
  • morpho: throws InsufficientBalanceError when token balance is too low
  • morpho: throws InsufficientBalanceError when token is absent from wallet (balance treated as 0)
  • morpho: does not throw when balance exactly equals requested amount
39 passing (81ms)
1 pending

Lint: ✅ TypeCheck: ✅ Full suite: ✅ (478 passing)

Open questions

None.

Out of scope

  • resumeDeposit — no balance check needed; the approve txRequest is already created at this point.
  • withdrawFromVault balance check (receipt/share token) is tracked in sibling ticket DEFI-372.

🤖 Generated with Ralph

Add InsufficientBalanceError and assertSufficientAssetBalance to the
DefiVault class. Before creating any txRequest, depositToVault now
fetches a fresh wallet snapshot (allTokens=true) and throws
InsufficientBalanceError if spendableBalance < amount.

For native-coin vaults (Concrete BTC: assetToken === coin), the top-
level spendableBalanceString is used. For token vaults (Morpho USDC/
USDT/ETH: assetToken !== coin), the tokens array is searched by
tokenName. Balance comparison uses BigNumber to avoid float precision
issues. Exports InsufficientBalanceError from the defi module index.

Ticket: DEFI-371
Session-Id: 39a66f44-5586-4260-9a85-c6fd946c9064
Task-Id: 48766e58-a805-49ca-b015-534a996101ac
@linear-code

linear-code Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

DEFI-371

@bitgo-ai-agent-dev
bitgo-ai-agent-dev Bot marked this pull request as draft July 20, 2026 12:15
@bitgo-ai-agent-dev

Copy link
Copy Markdown
Author

@claude please review this PR

@bitgo-ai-agent-dev
bitgo-ai-agent-dev Bot force-pushed the DEFI-371-pre-flight-balance-check-deposit branch from 8c61b6e to 8454f57 Compare July 20, 2026 12:15
@bitgo-ai-agent-dev
bitgo-ai-agent-dev Bot marked this pull request as ready for review July 20, 2026 12:15

@davidkaplanbitgo davidkaplanbitgo 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.

Change the code owners so that this does not require a BTC team review

* propagates network errors as-is.
*/
private async assertSufficientAssetBalance(config: GetVaultResponse, amount: string): Promise<void> {
const walletData: Record<string, any> = await this.bitgo.get(this.wallet.url()).query({ allTokens: true }).result();

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.

is there no better typing for the value we can infer here, as opposed to any?

* Error thrown when the wallet does not hold sufficient balance of the underlying asset
* to cover the requested deposit amount.
*/
export class InsufficientBalanceError extends Error {

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.

nit - consider moving this to an error.ts file

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