Docs

suix_getAllBalances - Get All Coin Balan...

Retrieve all coin balances for any Sui address using suix_getAllBalances RPC method. Get comprehensive portfolio overview with native SUI and custom tokens using Dwellir's high-performance Sui infrastructure.

Retrieves all coin balances for a specific address on the Sui blockchain, providing a comprehensive overview of all owned fungible tokens in a single request.

Overview

The suix_getAllBalances method is essential for portfolio management and wallet applications. Unlike suix_getBalance which queries a specific coin type, this method returns all coin types owned by an address, including native SUI tokens and any custom fungible tokens. This makes it perfect for displaying complete portfolio overviews, calculating total portfolio values, and discovering all assets owned by an address.

Code Examples

Common Use Cases

1. Portfolio Dashboard

JavaScript
async function buildPortfolioDashboard(address) {
  const balances = await client.getAllBalances({ owner: address });
  
  const dashboard = {
    totalAssets: balances.length,
    nonZeroAssets: 0,
    totalValue: 0, // Would need price data
    assets: []
  };
  
  for (const balance of balances) {
    const asset = {
      symbol: extractSymbol(balance.coinType),
      coinType: balance.coinType,
      balance: balance.totalBalance,
      objectCount: balance.coinObjectCount,
      hasLockedBalance: !!balance.lockedBalance
    };
    
    if (parseInt(balance.totalBalance) > 0) {
      dashboard.nonZeroAssets++;
    }
    
    dashboard.assets.push(asset);
  }
  
  // Sort by balance (highest first)
  dashboard.assets.sort((a, b) => 
    parseInt(b.balance) - parseInt(a.balance)
  );
  
  return dashboard;
}

2. Asset Discovery

JavaScript
async function discoverNewAssets(address, knownCoinTypes = []) {
  const balances = await client.getAllBalances({ owner: address });
  
  const newAssets = balances.filter(balance => 
    !knownCoinTypes.includes(balance.coinType) &&
    parseInt(balance.totalBalance) > 0
  );
  
  console.log(`Discovered ${newAssets.length} new assets:`);
  
  for (const asset of newAssets) {
    console.log(`- ${extractSymbol(asset.coinType)}: ${asset.totalBalance}`);
    
    // Optionally fetch metadata for new assets
    try {
      const metadata = await client.getCoinMetadata({
        coinType: asset.coinType
      });
      
      console.log(`  Metadata: ${metadata?.name || 'Unknown'} (${metadata?.symbol || 'N/A'})`);
    } catch (error) {
      console.log(`  Could not fetch metadata: ${error.message}`);
    }
  }
  
  return newAssets;
}

3. Balance Monitoring with Alerts

JavaScript
class BalanceMonitor {
  constructor(client) {
    this.client = client;
    this.thresholds = new Map();
    this.lastBalances = new Map();
  }
  
  setThreshold(address, coinType, threshold) {
    const key = `${address}:${coinType}`;
    this.thresholds.set(key, threshold);
  }
  
  async checkBalances(address) {
    const currentBalances = await this.client.getAllBalances({ owner: address });
    const alerts = [];
    
    for (const balance of currentBalances) {
      const key = `${address}:${balance.coinType}`;
      const threshold = this.thresholds.get(key);
      const currentAmount = parseInt(balance.totalBalance);
      const lastAmount = this.lastBalances.get(key) || 0;
      
      // Check threshold alerts
      if (threshold && currentAmount < threshold) {
        alerts.push({
          type: 'low_balance',
          coinType: balance.coinType,
          current: currentAmount,
          threshold: threshold,
          symbol: extractSymbol(balance.coinType)
        });
      }
      
      // Check for significant changes
      const percentChange = lastAmount > 0 ? 
        ((currentAmount - lastAmount) / lastAmount) * 100 : 0;
      
      if (Math.abs(percentChange) > 10) { // 10% change
        alerts.push({
          type: percentChange > 0 ? 'balance_increase' : 'balance_decrease',
          coinType: balance.coinType,
          current: currentAmount,
          previous: lastAmount,
          percentChange: percentChange,
          symbol: extractSymbol(balance.coinType)
        });
      }
      
      this.lastBalances.set(key, currentAmount);
    }
    
    return alerts;
  }
}

// Usage
const monitor = new BalanceMonitor(client);
monitor.setThreshold(address, '0x2::sui::SUI', 1_000_000_000); // 1 SUI minimum

const alerts = await monitor.checkBalances(address);
alerts.forEach(alert => {
  console.log(`Alert: ${alert.type} for ${alert.symbol}`);
});

4. Portfolio Value Calculator

JavaScript
async function calculatePortfolioValue(address, priceOracle) {
  const balances = await client.getAllBalances({ owner: address });
  let totalValue = 0;
  const breakdown = [];
  
  for (const balance of balances) {
    const amount = parseInt(balance.totalBalance);
    
    if (amount === 0) continue;
    
    try {
      // Get current price from oracle
      const price = await priceOracle.getPrice(balance.coinType);
      const decimals = await getDecimals(balance.coinType);
      const formattedAmount = amount / Math.pow(10, decimals);
      const value = formattedAmount * price;
      
      totalValue += value;
      
      breakdown.push({
        symbol: extractSymbol(balance.coinType),
        coinType: balance.coinType,
        amount: formattedAmount,
        price: price,
        value: value,
        percentage: 0 // Will be calculated after total
      });
    } catch (error) {
      console.warn(`Could not get price for ${balance.coinType}:`, error.message);
      
      breakdown.push({
        symbol: extractSymbol(balance.coinType),
        coinType: balance.coinType,
        amount: amount / Math.pow(10, 9), // Assume 9 decimals
        price: 0,
        value: 0,
        percentage: 0
      });
    }
  }
  
  // Calculate percentages
  breakdown.forEach(item => {
    item.percentage = totalValue > 0 ? (item.value / totalValue) * 100 : 0;
  });
  
  // Sort by value
  breakdown.sort((a, b) => b.value - a.value);
  
  return {
    totalValue,
    breakdown,
    lastUpdated: new Date().toISOString()
  };
}

Advanced Portfolio Management

1. Portfolio Rebalancing Suggestions

JavaScript
async function suggestRebalancing(address, targetAllocations) {
  const balances = await client.getAllBalances({ owner: address });
  const currentValue = await calculatePortfolioValue(address, priceOracle);
  
  const suggestions = [];
  
  for (const [coinType, targetPercent] of Object.entries(targetAllocations)) {
    const current = currentValue.breakdown.find(b => b.coinType === coinType);
    const currentPercent = current ? current.percentage : 0;
    const difference = targetPercent - currentPercent;
    
    if (Math.abs(difference) > 5) { // 5% threshold
      const targetValue = (currentValue.totalValue * targetPercent) / 100;
      const currentValue = current ? current.value : 0;
      const adjustment = targetValue - currentValue;
      
      suggestions.push({
        coinType: coinType,
        symbol: extractSymbol(coinType),
        currentPercent: currentPercent,
        targetPercent: targetPercent,
        difference: difference,
        action: adjustment > 0 ? 'buy' : 'sell',
        amount: Math.abs(adjustment)
      });
    }
  }
  
  return suggestions;
}

2. Historical Balance Tracking

JavaScript
class PortfolioHistoryTracker {
  constructor(client, storage) {
    this.client = client;
    this.storage = storage; // Database or local storage
  }
  
  async recordSnapshot(address) {
    const balances = await this.client.getAllBalances({ owner: address });
    const timestamp = Date.now();
    
    const snapshot = {
      address,
      timestamp,
      balances: balances.map(b => ({
        coinType: b.coinType,
        totalBalance: b.totalBalance,
        coinObjectCount: b.coinObjectCount,
        lockedBalance: b.lockedBalance
      }))
    };
    
    await this.storage.saveSnapshot(snapshot);
    return snapshot;
  }
  
  async getHistoricalData(address, days = 30) {
    const snapshots = await this.storage.getSnapshots(address, days);
    
    // Process into time series data
    const timeSeries = {};
    
    snapshots.forEach(snapshot => {
      snapshot.balances.forEach(balance => {
        if (!timeSeries[balance.coinType]) {
          timeSeries[balance.coinType] = [];
        }
        
        timeSeries[balance.coinType].push({
          timestamp: snapshot.timestamp,
          balance: balance.totalBalance,
          objectCount: balance.coinObjectCount
        });
      });
    });
    
    return timeSeries;
  }
  
  async generateGrowthReport(address, days = 30) {
    const historyData = await this.getHistoricalData(address, days);
    const report = {
      period: days,
      assets: {}
    };
    
    for (const [coinType, data] of Object.entries(historyData)) {
      if (data.length < 2) continue;
      
      const oldest = data[0];
      const newest = data[data.length - 1];
      const growth = parseInt(newest.balance) - parseInt(oldest.balance);
      const growthPercent = parseInt(oldest.balance) > 0 ? 
        (growth / parseInt(oldest.balance)) * 100 : 0;
      
      report.assets[coinType] = {
        symbol: extractSymbol(coinType),
        startBalance: oldest.balance,
        endBalance: newest.balance,
        absoluteGrowth: growth.toString(),
        percentGrowth: growthPercent,
        dataPoints: data.length
      };
    }
    
    return report;
  }
}

Performance Optimization

1. Efficient Portfolio Polling

JavaScript
class OptimizedPortfolioFetcher {
  constructor(client) {
    this.client = client;
    this.cache = new Map();
    this.cacheTimeout = 30000; // 30 seconds
  }
  
  async getBalancesWithCache(address) {
    const cacheKey = address;
    const cached = this.cache.get(cacheKey);
    
    if (cached && (Date.now() - cached.timestamp) < this.cacheTimeout) {
      return cached.balances;
    }
    
    const balances = await this.client.getAllBalances({ owner: address });
    
    this.cache.set(cacheKey, {
      balances,
      timestamp: Date.now()
    });
    
    return balances;
  }
  
  async batchGetBalances(addresses) {
    const promises = addresses.map(address => 
      this.getBalancesWithCache(address).catch(error => ({
        address,
        error: error.message
      }))
    );
    
    const results = await Promise.all(promises);
    return results;
  }
  
  clearCache() {
    this.cache.clear();
  }
}

2. Smart Update Detection

JavaScript
async function detectPortfolioUpdates(address, lastKnownBalances) {
  const currentBalances = await client.getAllBalances({ owner: address });
  
  // Quick check - compare array lengths and coin types
  if (currentBalances.length !== lastKnownBalances.length) {
    return { hasUpdates: true, reason: 'coin_count_changed' };
  }
  
  const currentTypes = new Set(currentBalances.map(b => b.coinType));
  const lastTypes = new Set(lastKnownBalances.map(b => b.coinType));
  
  if (currentTypes.size !== lastTypes.size || 
      ![...currentTypes].every(type => lastTypes.has(type))) {
    return { hasUpdates: true, reason: 'coin_types_changed' };
  }
  
  // Check individual balances
  for (let i = 0; i < currentBalances.length; i++) {
    const current = currentBalances[i];
    const last = lastKnownBalances.find(b => b.coinType === current.coinType);
    
    if (!last || current.totalBalance !== last.totalBalance) {
      return { 
        hasUpdates: true, 
        reason: 'balance_changed',
        changedCoin: current.coinType
      };
    }
  }
  
  return { hasUpdates: false };
}

Error Handling and Edge Cases

1. Comprehensive Error Handling

JavaScript
async function safeGetAllBalances(address) {
  try {
    // Validate address format
    if (!/^0x[a-fA-F0-9]{64}$/.test(address)) {
      throw new Error('Invalid Sui address format');
    }
    
    const balances = await client.getAllBalances({ owner: address });
    
    // Validate response structure
    if (!Array.isArray(balances)) {
      throw new Error('Invalid response: expected array of balances');
    }
    
    // Filter out invalid entries
    const validBalances = balances.filter(balance => 
      balance.coinType && 
      typeof balance.totalBalance === 'string' &&
      typeof balance.coinObjectCount === 'number'
    );
    
    if (validBalances.length !== balances.length) {
      console.warn(`Filtered ${balances.length - validBalances.length} invalid balance entries`);
    }
    
    return {
      success: true,
      balances: validBalances,
      metadata: {
        totalTypes: validBalances.length,
        hasLockedBalances: validBalances.some(b => b.lockedBalance),
        timestamp: Date.now()
      }
    };
    
  } catch (error) {
    return {
      success: false,
      error: error.message,
      balances: [],
      metadata: {
        timestamp: Date.now()
      }
    };
  }
}

2. Handle Edge Cases

JavaScript
function processBalanceEdgeCases(balances) {
  const processed = {
    valid: [],
    zeroBalances: [],
    dustBalances: [],
    lockedOnly: [],
    issues: []
  };
  
  balances.forEach((balance, index) => {
    const amount = parseInt(balance.totalBalance);
    
    // Check for zero balances
    if (amount === 0) {
      processed.zeroBalances.push(balance);
      return;
    }
    
    // Check for dust (very small amounts)
    if (amount < 1000) { // Less than 0.000001 for 9-decimal coins
      processed.dustBalances.push(balance);
    }
    
    // Check locked-only balances
    if (balance.lockedBalance && 
        parseInt(balance.lockedBalance.number) >= amount) {
      processed.lockedOnly.push(balance);
    }
    
    // Check for potential issues
    if (balance.coinObjectCount === 0 && amount > 0) {
      processed.issues.push({
        index,
        issue: 'positive_balance_zero_objects',
        balance
      });
    }
    
    if (balance.coinObjectCount > 100) {
      processed.issues.push({
        index,
        issue: 'too_many_coin_objects',
        balance
      });
    }
    
    processed.valid.push(balance);
  });
  
  return processed;
}

Best Practices

1. Efficient Data Processing

  • Filter zero balances when displaying portfolio views
  • Cache coin metadata to avoid repeated RPC calls
  • Use pagination for addresses with many coin types
  • Implement progressive loading for better user experience

2. Portfolio Management

  • Track historical snapshots for growth analysis
  • Set up balance alerts for important thresholds
  • Monitor locked balances for staking and DeFi positions
  • Aggregate small balances to reduce UI clutter

3. Performance Optimization

  • Cache results for frequently queried addresses
  • Batch multiple address queries when possible
  • Use WebSocket subscriptions for real-time updates
  • Implement smart update detection to minimize unnecessary calls

4. Error Resilience

  • Validate address format before making requests
  • Handle network timeouts gracefully
  • Retry failed requests with exponential backoff
  • Filter invalid response data to prevent application errors

5. User Experience

  • Display loading states during data fetching
  • Show meaningful error messages to users
  • Format balances according to coin decimals
  • Highlight significant changes in portfolio values

Need help? Contact our support team or check the Sui documentation.