Skip to main content

Overview

The Balances API provides a unified view of a user’s token holdings across all supported chains. It aggregates balances of the same asset type (e.g., USDC on Ethereum, Polygon, and Arbitrum) and provides both crypto and fiat valuations.

API Methods

getAggregatedBalance

Retrieve aggregated balances for all assets held by an account.
balancesApi.getAggregatedBalance(address: string): Promise<BalancesResponse>

Parameters

address
string
required
The user’s smart contract account address (predicted address from OneBalance)
This should be the predicted/smart account address, not the embedded wallet address.

Returns

BalancesResponse
object
Complete balance information across all chains and assets

Example

import { balancesApi } from '@/lib/api/balances';

const accountAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balances = await balancesApi.getAggregatedBalance(accountAddress);

console.log('Total portfolio value:', balances.totalBalance.fiatValue, 'USD');

// Display each asset
balances.balanceByAggregatedAsset.forEach(asset => {
  console.log(`${asset.aggregatedAssetId}: $${asset.fiatValue}`);
  console.log(`  Balance: ${asset.balance} (${asset.symbol})`);
  
  // Show breakdown by chain
  asset.individualAssetBalances.forEach(individual => {
    console.log(`    ${individual.assetType}: ${individual.balance}`);
  });
});

Understanding Aggregated Balances

OneBalance aggregates the same token across different chains. For example:
{
  "balanceByAggregatedAsset": [
    {
      "aggregatedAssetId": "ob:usdc",
      "balance": "150000000",
      "fiatValue": 150.00,
      "symbol": "USDC",
      "decimals": 6,
      "individualAssetBalances": [
        {
          "assetType": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
          "balance": "50000000",
          "fiatValue": 50.00
        },
        {
          "assetType": "eip155:137/erc20:0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
          "balance": "75000000",
          "fiatValue": 75.00
        },
        {
          "assetType": "eip155:42161/erc20:0xff970a61a04b1ca14834a43f5de4533ebddb5cc8",
          "balance": "25000000",
          "fiatValue": 25.00
        }
      ]
    },
    {
      "aggregatedAssetId": "ob:eth",
      "balance": "500000000000000000",
      "fiatValue": 1850.00,
      "symbol": "ETH",
      "decimals": 18,
      "individualAssetBalances": [
        {
          "assetType": "eip155:1/slip44:60",
          "balance": "300000000000000000",
          "fiatValue": 1110.00
        },
        {
          "assetType": "eip155:10/slip44:60",
          "balance": "200000000000000000",
          "fiatValue": 740.00
        }
      ]
    }
  ],
  "totalBalance": {
    "fiatValue": 2000.00
  }
}
This shows:
  • 150 USDC total across 3 chains (Ethereum, Polygon, Arbitrum)
  • 0.5 ETH total across 2 chains (Ethereum, Optimism)
  • Total portfolio value: $2,000

Formatting Balances for Display

import { formatUnits } from 'viem';

function formatAssetBalance(asset: BalanceByAssetDto) {
  const decimals = asset.decimals || 18;
  const formatted = formatUnits(BigInt(asset.balance), decimals);
  
  return {
    amount: formatted,
    symbol: asset.symbol,
    usdValue: asset.fiatValue.toFixed(2),
    displayText: `${parseFloat(formatted).toFixed(4)} ${asset.symbol}`
  };
}

// Usage
balances.balanceByAggregatedAsset.forEach(asset => {
  const formatted = formatAssetBalance(asset);
  console.log(`${formatted.displayText} ($${formatted.usdValue})`);
});

Real-World Implementation

Here’s how balances are fetched and managed in the actual application:
const fetchBalances = async () => {
  if (!predictedAddress) {
    return;
  }

  setLoading(true);
  setError(null);

  try {
    const data = await balancesApi.getAggregatedBalance(predictedAddress);
    setBalances(data);
  } catch (err) {
    setError(err instanceof Error ? err.message : 'Failed to fetch balances');
  } finally {
    setLoading(false);
  }
};

Understanding CAIP-19 Asset Types

Individual asset types use the CAIP-19 standard:
{namespace}:{chainId}/{assetType}:{assetId}

Examples:
eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
├─ eip155: Ethereum namespace
├─ 1: Ethereum Mainnet
├─ erc20: Token standard
└─ 0xa0b8...: USDC contract address

eip155:1/slip44:60
├─ eip155: Ethereum namespace  
├─ 1: Ethereum Mainnet
├─ slip44: Native asset
└─ 60: ETH coin type

Filtering and Sorting

const nonZeroBalances = balances.balanceByAggregatedAsset.filter(
  asset => BigInt(asset.balance) > 0n
);

Calculating Totals

function getAssetPercentage(
  asset: BalanceByAssetDto,
  totalBalance: TotalBalance
): number {
  if (totalBalance.fiatValue === 0) return 0;
  return (asset.fiatValue / totalBalance.fiatValue) * 100;
}

// Usage
balances.balanceByAggregatedAsset.forEach(asset => {
  const percentage = getAssetPercentage(asset, balances.totalBalance);
  console.log(`${asset.symbol}: ${percentage.toFixed(2)}%`);
});

Error Handling

try {
  const balances = await balancesApi.getAggregatedBalance(address);
  return balances;
} catch (error) {
  if (error instanceof Error) {
    console.error('Failed to fetch balances:', error.message);
    
    if (error.message.includes('invalid address')) {
      // Show address validation error
    } else if (error.message.includes('not found')) {
      // Account has no balances yet
      return {
        balanceByAggregatedAsset: [],
        totalBalance: { fiatValue: 0 }
      };
    }
  }
  
  throw error;
}

Refresh Strategies

On Navigation

Refresh balances when user navigates to balance/portfolio pages

After Transactions

Automatically refresh after successful swaps or transfers

Periodic Updates

Set up periodic refresh (e.g., every 30 seconds) when page is active

Manual Refresh

Provide pull-to-refresh or refresh button for user-initiated updates

Best Practices

Cache Balances

Cache balance data to avoid unnecessary API calls

Show Loading States

Display skeleton loaders while fetching balances

Handle Zero Balances

Show helpful empty states for new accounts

Format Consistently

Use consistent decimal places and currency formatting

Build docs developers (and LLMs) love