Skip to main content
The Balance Service provides functions for fetching token balances across chains, with built-in caching, validation, and error handling.

Exports

import {
  getBalance,
  getBalances,
  getChainBalances,
  getBalanceWithMetadata,
  validateBalance,
  checkBalance,
  createBalanceCache,
  BalanceCache,
  BalanceFetchError,
  InvalidAddressError,
  isBalanceFetchError,
  isInvalidAddressError,
} from '@mina-bridge/sdk/services/balance';

Functions

getBalance(chainId, tokenAddress, walletAddress, cache?)

Get token balance for a specific wallet address.
async function getBalance(
  chainId: number,
  tokenAddress: string,
  walletAddress: string,
  cache?: BalanceCache
): Promise<Balance>
ParameterTypeRequiredDescription
chainIdnumberYesChain ID
tokenAddressstringYesToken contract address
walletAddressstringYesWallet address to check
cacheBalanceCacheNoOptional cache instance
Returns: Balance
PropertyTypeDescription
tokenTokenToken metadata
balancestringBalance in smallest unit
formattedstringBalance formatted with decimals
balanceUsdnumber | undefinedBalance in USD
Throws:
  • InvalidAddressError if the wallet address is invalid
  • BalanceFetchError if the API fails and no cache available
Example:
import { getBalance, NATIVE_TOKEN_ADDRESS } from '@mina-bridge/sdk';

// Get USDC balance on Ethereum
const usdcBalance = await getBalance(
  1,
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  '0xYourWalletAddress...'
);

console.log(`USDC: ${usdcBalance.formatted}`);       // "1234.56"
console.log(`USD Value: $${usdcBalance.balanceUsd}`); // "1234.56"

// Get native ETH balance
const ethBalance = await getBalance(
  1,
  NATIVE_TOKEN_ADDRESS,
  '0xYourWalletAddress...'
);

console.log(`ETH: ${ethBalance.formatted}`);

getBalances(walletAddress, chainIds, tokenAddresses?, cache?)

Get token balances across multiple chains in parallel.
async function getBalances(
  walletAddress: string,
  chainIds: number[],
  tokenAddresses?: Record<number, string[]>,
  cache?: BalanceCache
): Promise<BalancesResponse>
ParameterTypeRequiredDescription
walletAddressstringYesWallet address to check
chainIdsnumber[]YesArray of chain IDs
tokenAddressesRecord<number, string[]>NoToken addresses per chain
cacheBalanceCacheNoOptional cache instance
Returns: BalancesResponse
PropertyTypeDescription
balancesRecord<number, Balance[]>Balances grouped by chain ID
totalUsdnumberTotal value in USD
isStalebooleanWhether any data is from stale cache
Example:
// Get native token balances on multiple chains
const response = await getBalances(
  '0x...',
  [1, 42161, 137]  // Ethereum, Arbitrum, Polygon
);

console.log(`Total portfolio: $${response.totalUsd.toFixed(2)}`);

// Access balances by chain
Object.entries(response.balances).forEach(([chainId, balances]) => {
  console.log(`Chain ${chainId}:`);
  balances.forEach(b => {
    console.log(`  ${b.token.symbol}: ${b.formatted}`);
  });
});
// Get specific tokens per chain
const response = await getBalances(
  '0x...',
  [1, 42161],
  {
    1: [
      '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
      '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
    ],
    42161: [
      '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
    ],
  }
);

getChainBalances(walletAddress, chainId, cache?)

Get all supported token balances for a specific chain.
async function getChainBalances(
  walletAddress: string,
  chainId: number,
  cache?: BalanceCache
): Promise<BalanceWithMetadata[]>
ParameterTypeRequiredDescription
walletAddressstringYesWallet address to check
chainIdnumberYesChain ID
cacheBalanceCacheNoOptional cache instance
Returns: Array of BalanceWithMetadata with extended information. Example:
const balances = await getChainBalances('0x...', 1);

// Filter to tokens with non-zero balance
const nonZeroBalances = balances.filter(b => b.hasBalance);

console.log(`You have ${nonZeroBalances.length} tokens on Ethereum:`);
nonZeroBalances.forEach(b => {
  console.log(`  ${b.token.symbol}: ${b.formatted} ($${b.balanceUsd ?? 0})`);
});

getBalanceWithMetadata(chainId, tokenAddress, walletAddress, cache?)

Get balance with additional metadata like USD value and price.
async function getBalanceWithMetadata(
  chainId: number,
  tokenAddress: string,
  walletAddress: string,
  cache?: BalanceCache
): Promise<BalanceWithMetadata>
Returns: BalanceWithMetadata
PropertyTypeDescription
tokenTokenToken metadata
balancestringBalance in smallest unit
formattedstringBalance formatted with decimals
balanceUsdnumber | undefinedBalance in USD
hasBalancebooleanWhether balance is greater than 0
chainIdnumberChain ID
priceUsdnumber | undefinedCurrent token price in USD
Example:
const balance = await getBalanceWithMetadata(
  1,
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  '0x...'
);

if (balance.hasBalance) {
  console.log(`Amount: ${balance.formatted} ${balance.token.symbol}`);
  console.log(`Price: $${balance.priceUsd}`);
  console.log(`Value: $${balance.balanceUsd}`);
}

validateBalance(quote, walletAddress, cache?)

Validate that a user has sufficient balance to execute a quote.
async function validateBalance(
  quote: Quote,
  walletAddress: string,
  cache?: BalanceCache
): Promise<BalanceValidation>
ParameterTypeRequiredDescription
quoteQuoteYesQuote to validate against
walletAddressstringYesWallet address to check
cacheBalanceCacheNoOptional cache instance
Returns: BalanceValidation
PropertyTypeDescription
validbooleanWhether validation passed
warningsBalanceWarning[]Array of warnings
tokenBalanceBalanceSource token balance
gasBalanceBalanceNative token balance for gas
BalanceWarning Types:
TypeDescription
INSUFFICIENT_BALANCENot enough tokens to bridge
INSUFFICIENT_GASNot enough native token for gas
LOW_GASGas balance is low (< 2x estimated)
Example:
const quote = await mina.getQuote({ ... });
const validation = await validateBalance(quote, '0x...');

if (!validation.valid) {
  validation.warnings.forEach(warning => {
    switch (warning.type) {
      case 'INSUFFICIENT_BALANCE':
        console.error(
          `Need ${warning.shortfall} more ${warning.token.symbol}`
        );
        break;
      case 'INSUFFICIENT_GAS':
        console.error(
          `Need ${warning.shortfall} more ${warning.token.symbol} for gas`
        );
        break;
      case 'LOW_GAS':
        console.warn(warning.message);
        break;
    }
  });
}

checkBalance(chainId, tokenAddress, walletAddress, amount, cache?)

Quick check if a user has sufficient balance for a specific amount.
async function checkBalance(
  chainId: number,
  tokenAddress: string,
  walletAddress: string,
  amount: string,
  cache?: BalanceCache
): Promise<BalanceCheckResult>
ParameterTypeRequiredDescription
chainIdnumberYesChain ID
tokenAddressstringYesToken address
walletAddressstringYesWallet address
amountstringYesAmount to check (smallest unit)
cacheBalanceCacheNoOptional cache instance
Returns: BalanceCheckResult
PropertyTypeDescription
sufficientbooleanWhether balance is sufficient
balancestringCurrent balance
formattedstringFormatted balance
requiredstringRequired amount
shortfallstring | undefinedAmount short (if insufficient)
Example:
// Check before showing quote UI
const check = await checkBalance(
  1,
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  '0x...',
  '1000000000' // 1000 USDC (6 decimals)
);

if (!check.sufficient) {
  console.log(`Balance: ${check.formatted}`);
  console.log(`Required: ${check.required}`);
  console.log(`Shortfall: ${check.shortfall}`);
} else {
  console.log('Sufficient balance to proceed');
}

Error Types

BalanceFetchError

Thrown when balance fetching fails.
class BalanceFetchError extends MinaError {
  readonly code: 'BALANCE_FETCH_FAILED';
  readonly chainId: number;
  readonly tokenAddress: string;
  readonly recoverable: true;
  readonly recoveryAction: 'retry';
  readonly cachedAvailable: boolean;
}
Example:
try {
  const balance = await getBalance(1, tokenAddress, walletAddress);
} catch (error) {
  if (isBalanceFetchError(error)) {
    console.error(`Failed to fetch balance for chain ${error.chainId}`);
    if (error.cachedAvailable) {
      console.log('Cached data may be available');
    }
  }
}

InvalidAddressError

Thrown when an invalid wallet address is provided.
class InvalidAddressError extends MinaError {
  readonly code: 'INVALID_ADDRESS';
  readonly address: string;
  readonly recoverable: false;
}
Example:
try {
  const balance = await getBalance(1, tokenAddress, 'invalid-address');
} catch (error) {
  if (isInvalidAddressError(error)) {
    console.error(`Invalid address: ${error.address}`);
  }
}

BalanceCache Class

The BalanceCache class manages caching for balance data with a 30-second TTL.

Methods

class BalanceCache {
  /** Get cached balance if not expired */
  getBalance(
    chainId: number,
    tokenAddress: string,
    walletAddress: string
  ): { data: Balance; cachedAt: number } | null;

  /** Store balance in cache */
  setBalance(
    chainId: number,
    tokenAddress: string,
    walletAddress: string,
    balance: Balance
  ): void;

  /** Invalidate cache for a wallet or all */
  invalidate(walletAddress?: string): void;

  /** Check if cached balance exists (even if expired) */
  hasCached(
    chainId: number,
    tokenAddress: string,
    walletAddress: string
  ): boolean;

  /** Get balance even if expired (for fallback) */
  getBalanceStale(
    chainId: number,
    tokenAddress: string,
    walletAddress: string
  ): { data: Balance; cachedAt: number } | null;
}

Example Usage

const cache = createBalanceCache();

// First request - fetches from API
const balance1 = await getBalance(1, tokenAddr, walletAddr, cache);

// Second request within 30 seconds - uses cache
const balance2 = await getBalance(1, tokenAddr, walletAddr, cache);

// Force refresh for specific wallet
cache.invalidate('0x...');

// Force refresh all
cache.invalidate();

Types

Balance

interface Balance {
  /** Token metadata */
  token: Token;
  /** Balance in smallest unit */
  balance: string;
  /** Balance formatted with decimals */
  formatted: string;
  /** Balance in USD */
  balanceUsd?: number;
}

BalanceWithMetadata

interface BalanceWithMetadata extends Balance {
  /** Whether balance is greater than 0 */
  hasBalance: boolean;
  /** Chain ID */
  chainId: number;
  /** Current token price in USD */
  priceUsd?: number;
}

BalanceValidation

interface BalanceValidation {
  /** Whether validation passed */
  valid: boolean;
  /** Array of warnings */
  warnings: BalanceWarning[];
  /** Source token balance */
  tokenBalance: Balance;
  /** Native token balance for gas */
  gasBalance: Balance;
}

BalanceWarning

interface BalanceWarning {
  type: 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'LOW_GAS';
  message: string;
  token: Token;
  required?: string;
  available: string;
  shortfall?: string;
}

BalanceCheckResult

interface BalanceCheckResult {
  /** Whether balance is sufficient */
  sufficient: boolean;
  /** Current balance in smallest unit */
  balance: string;
  /** Formatted balance */
  formatted: string;
  /** Required amount */
  required: string;
  /** Amount short (if insufficient) */
  shortfall?: string;
}