Skip to main content
Rainbow provides comprehensive asset management across multiple blockchains, with real-time pricing, filtering, and organization features.

Asset Architecture

Rainbow uses a sophisticated asset store with query-based data fetching:
// From src/state/assets/types.ts
interface UserAssetsState {
  address: Address | string;
  userAssets: Map<UniqueId, ParsedSearchAsset>;
  chainBalances: Map<ChainId, number>;
  filter: UserAssetFilter;
  hiddenAssets: Set<UniqueId>;
  searchCache: Map<string, UniqueId[]>;
  
  getUserAssets: () => ParsedSearchAsset[];
  getUserAsset: (uniqueId: UniqueId) => ParsedSearchAsset | null;
  getTotalBalance: () => number;
  getFilteredUserAssetIds: () => UniqueId[];
}

Asset Structure

type Asset = {
  address: string;           // Token contract address
  chainId: number;          // Blockchain network ID
  name: string;             // Token name
  symbol: string;           // Token symbol (ETH, USDC, etc.)
  decimals: number;         // Token decimals (18 for ETH)
  type: string;             // 'token' | 'nft'
  iconUrl?: string;         // Token logo URL
  network: string;          // Network name
  verified: boolean;        // Verified by Rainbow
  transferable: boolean;    // Can be transferred
  probableSpam?: boolean;   // Spam detection flag
  
  // Pricing data
  price: {
    value: number;          // Current price in USD
    changedAt: number;      // Last price update timestamp
    relativeChange24h: number; // 24h price change %
  };
  
  // Multi-chain support
  networks: Record<string, {
    address: string;
    decimals: number;
  }>;
  
  // Bridging capability
  bridging: {
    bridgeable: boolean;
    networks: Record<string, {
      bridgeable: boolean;
    }>;
  };
  
  // Color theming
  colors: {
    primary: string;
    fallback?: string;
  };
};

User Asset (with balance)

type UserAsset = {
  asset: Asset;
  quantity: string;         // Token balance
  updatedAt: string;       // Last balance update
  value: string;           // USD value of holdings
  smallBalance?: boolean;  // Flag for dust
};

type EnrichedAsset = Asset & {
  balance: {
    amount: string;        // Raw balance
    display: string;       // Formatted for display
  };
  native: {
    balance: {
      amount: string;      // USD value
      display: string;     // Formatted USD
    };
    price?: {
      change: string;      // 24h change
      amount: number;      // Current price
      display: string;     // Formatted price
    };
  };
};

Supported Networks

Rainbow supports assets across multiple chains:
  • Ethereum (Mainnet)
  • Polygon
  • Optimism
  • Arbitrum
  • Base
  • BSC (Binance Smart Chain)
  • Avalanche
  • Zora
  • Blast
  • Degen
Network support may vary by feature. Check specific documentation for swap and bridge availability per chain.

Viewing Assets

Asset List

Assets are displayed with:
  • Token icon and symbol
  • Balance amount
  • USD value
  • 24h price change
  • Chain indicator

Filtering Options

type UserAssetFilter = 
  | 'all'           // All assets
  | 'verified'      // Verified tokens only
  | 'unverified'    // Unverified tokens
  | 'small'         // Small balance (dust)
  | 'hidden';       // Hidden assets
Users can filter by:
Shows every token in wallet:
  • Verified and unverified
  • All balances
  • All chains
  • Default view

Asset Fetching

Assets are fetched from Rainbow backend:
type UserAssetsParams = {
  address: Address | string;  // Wallet address
  currency: SupportedCurrencyKey; // Display currency (USD, EUR, etc.)
  testnetMode: boolean;       // Include testnet assets
};

type GetAssetsResponse = {
  metadata: {
    requestTime: string;
    responseTime: string;
    requestId: string;
    currency: string;
    success: boolean;
    chainIdsWithErrors?: ChainId[]; // Failed chains
  };
  result?: Record<string, UserAsset>; // Assets by unique ID
  errors: { chainId: ChainId; error: string }[]; // Per-chain errors
};

Fetching Process

1

Trigger Fetch

Assets fetch when:
  • Wallet loads
  • Account switches
  • Manual refresh
  • Background sync (periodic)
2

Multi-Chain Request

Single API call fetches all chains:
  • Parallel requests to each chain
  • Aggregated response
  • Partial success handling
3

Process Response

Assets are:
  • Parsed into typed objects
  • Enriched with display data
  • Stored in asset map
  • Indexed by chain
4

Update UI

UI reactively updates:
  • Balance totals recalculated
  • Price changes shown
  • Charts updated
  • Notifications for changes

Chain Balances

Rainbow tracks balance per chain:
getBalanceSortedChainList: () => ChainId[] {
  const { chainBalances } = get();
  
  return Array.from(chainBalances.entries())
    .sort(([, a], [, b]) => b - a) // Sort by balance descending
    .map(([chainId]) => chainId);
}

getChainsWithBalance: () => ChainId[] {
  const { chainBalances } = get();
  
  return Array.from(chainBalances.entries())
    .filter(([, balance]) => balance > 0)
    .map(([chainId]) => chainId);
}

Per-Chain Display

Users can view:
  • Total balance per chain
  • Chain ranking by value
  • Assets on each chain
  • Chain-specific actions (swap, bridge)

Hidden Assets

Users can hide unwanted tokens:
setHiddenAssets: (uniqueIds: UniqueId[]) => {
  const hiddenAssets = new Set(uniqueIds);
  set({ hiddenAssets });
  
  // Recalculate hidden balance
  const hiddenBalance = uniqueIds.reduce((sum, id) => {
    const asset = get().getUserAsset(id);
    return sum + (asset?.native?.balance?.amount || 0);
  }, 0);
  
  set({ hiddenAssetsBalance: hiddenBalance.toString() });
}

Hidden Asset Features

  • Hide from view: Removed from main asset list
  • Still counted: Included in total portfolio value
  • Easy unhide: Can reveal hidden assets anytime
  • Spam management: Hide spam/scam tokens
Fast search with caching:
setSearchQuery: (query: string) => {
  set({ inputSearchQuery: query });
  
  // Check cache first
  const cached = get().searchCache.get(query);
  if (cached) return cached;
  
  // Filter assets
  const filtered = Array.from(get().userAssets.values())
    .filter(asset => {
      const searchLower = query.toLowerCase();
      return (
        asset.symbol.toLowerCase().includes(searchLower) ||
        asset.name.toLowerCase().includes(searchLower) ||
        asset.address.toLowerCase().includes(searchLower)
      );
    })
    .map(asset => asset.uniqueId);
  
  // Cache result
  get().setSearchCache(query, filtered);
  
  return filtered;
}
Search supports:
  • Token symbol (ETH, USDC)
  • Token name (Ethereum, USD Coin)
  • Contract address
  • Fuzzy matching

Live Price Updates

Prices update in real-time:
updateTokens: (tokens: LiveTokensData) => {
  const { userAssets } = get();
  
  // Update each asset with new price
  tokens.forEach(({ uniqueId, price, change24h }) => {
    const asset = userAssets.get(uniqueId);
    if (!asset) return;
    
    // Update price data
    const updated = {
      ...asset,
      price: {
        value: price,
        changedAt: Date.now(),
        relativeChange24h: change24h,
      },
    };
    
    userAssets.set(uniqueId, updated);
  });
  
  // Recalculate balances
  get().reprocessAssetsData();
}

Price Change Display

  • Green: Positive 24h change
  • Red: Negative 24h change
  • Percentage: Change displayed as %
  • Sparkline: Mini chart showing price trend

Total Balance Calculation

getTotalBalance: () => {
  const { userAssets, hiddenAssets, filter } = get();
  
  let total = 0;
  
  for (const [uniqueId, asset] of userAssets.entries()) {
    // Skip hidden if filter set
    if (filter !== 'hidden' && hiddenAssets.has(uniqueId)) {
      continue;
    }
    
    // Add asset value
    const value = parseFloat(asset.native?.balance?.amount || '0');
    total += value;
  }
  
  return total;
}
Total includes:
  • All visible assets
  • All chains
  • Current prices
  • Hidden assets (if filter allows)

Asset Actions

For each asset, users can:

Send

Transfer tokens to another address

Swap

Exchange for another token

Bridge

Move to different blockchain

Hide

Remove from main view

Spam Detection

Rainbow flags probable spam tokens:
if (asset.probableSpam) {
  // Display warning
  // Require confirmation for actions
  // Auto-hide option
}
Spam indicators:
  • Unverified token
  • Suspicious name/symbol
  • Very low value
  • Recent airdrop
  • Copycat token
Always verify token contracts before interacting with unverified tokens. Scammers often airdrop fake tokens to steal funds through malicious contracts.

Performance Optimizations

Lazy Loading

  • Assets loaded as needed
  • Virtualized lists for long asset lists
  • Image lazy loading

Caching

  • Search results cached
  • Price data cached
  • Asset metadata cached
  • Selective invalidation

Selective Updates

  • Only changed assets re-render
  • Debounced price updates
  • Background refresh

Token List

Managing custom token lists

NFTs

Viewing and managing NFT collections

Collectibles

Digital collectibles management

Swaps

Swapping between assets

Build docs developers (and LLMs) love