Skip to main content
Bridge Wrapped aggregates token information from multiple sources to provide accurate symbol mapping, decimal precision, and visual assets for bridged tokens.

Token data service

The CoinMarketCapService class (src/services/tokens/coinmarketcap.ts) manages token metadata with intelligent fallback mechanisms.
interface TokenInfo {
  symbol: string;      // Token symbol (e.g., "USDC")
  name: string;        // Full token name (e.g., "USD Coin")
  decimals: number;    // Token precision (6 for USDC, 18 for ETH)
  logo?: string;       // Logo image URL
}

Data sources

Primary: Bridge API responses

Token information is first extracted from bridge provider APIs:
const tokenSymbol = deposit.token?.symbol || deposit.inputToken;
const tokenDecimals = deposit.token?.decimals || 18;
const tokenPriceUSD = parseFloat(deposit.inputPriceUsd);

Fallback: Hardcoded token mappings

For common tokens, the service maintains hardcoded mappings across multiple chains:

Native and wrapped ETH

// ETH (native address)
if (lowerAddress === '0x0000000000000000000000000000000000000000' ||
    lowerAddress === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee') {
  return {
    symbol: 'ETH',
    name: 'Ethereum',
    decimals: 18,
    logo: 'https://cryptologos.cc/logos/ethereum-eth-logo.png'
  };
}

// WETH addresses across chains
const wethAddresses = [
  '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',  // Ethereum mainnet
  '0x4200000000000000000000000000000000000006',  // Base, Optimism, Mode
  '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',  // Arbitrum
  '0x5300000000000000000000000000000000000004',  // Scroll
];

Stablecoins

const usdcAddresses = [
  '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',  // Ethereum
  '0xaf88d065e77c8cc2239327c5edb3a432268e5831',  // Arbitrum
  '0x0b2c639c533813f4aa9d7837caf62653d097ff85',  // Optimism
  '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',  // Base
  '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',  // Polygon
  '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',  // Polygon (old)
  // ... 10+ more chain addresses
];

return {
  symbol: 'USDC',
  name: 'USD Coin',
  decimals: 6,  // Important: 6 decimals, not 18!
  logo: 'https://coin-images.coingecko.com/coins/images/6319/large/usdc.png'
};

Tertiary: CoinMarketCap API

For unknown tokens, the service queries the CoinMarketCap API:
async getTokenInfo(address: string): Promise<TokenInfo | null> {
  // Check cache first
  const cached = this.cache.get(lowerAddress);
  if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
    return cached.data;
  }
  
  // Try fallback mappings
  const fallbackInfo = this.getFallbackTokenInfo(address);
  if (fallbackInfo) {
    this.cache.set(lowerAddress, { data: fallbackInfo, timestamp: Date.now() });
    return fallbackInfo;
  }
  
  // Query CoinMarketCap API
  const response = await this.fetchFromAPI([lowerAddress]);
  if (response && response.status.error_code === 0) {
    const tokenData = Object.values(response.data)[0];
    // ... process and cache
  }
}

Caching strategy

Token information is cached for 1 hour to minimize API calls:
private cache: Map<string, { data: TokenInfo; timestamp: number }> = new Map();
private cacheExpiry = 1000 * 60 * 60; // 1 hour

// Check if cached data is still valid
if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
  return cached.data;
}
The cache is stored in-memory and persists for the duration of the user’s session.

Token amount parsing

Token amounts are parsed from smallest units (wei) to human-readable decimals:
import { parseTokenAmount } from '@/lib/utils';

// Convert raw amount to formatted decimal
const amountFormatted = parseTokenAmount(
  '1000000',  // 1 USDC in raw units
  6           // USDC has 6 decimals
);
// Returns: 1.0

const amountFormatted = parseTokenAmount(
  '1000000000000000000',  // 1 ETH in wei
  18                      // ETH has 18 decimals
);
// Returns: 1.0

Price information

Price sources

Token prices (USD valuations) come exclusively from bridge provider APIs:
Bridge Wrapped does NOT fetch current token prices. All USD values reflect the price at the time of the bridge transaction as reported by the bridge protocol.
let tokenPriceUSD = 0;
if (deposit.inputPriceUsd) {
  tokenPriceUSD = parseFloat(deposit.inputPriceUsd);
} else if (deposit.token?.priceUsd) {
  tokenPriceUSD = parseFloat(deposit.token.priceUsd);
}

const amountUSD = amountFormatted * tokenPriceUSD;

Token statistics

The aggregator calculates token usage statistics:
interface TokenStats {
  symbol: string;           // Token symbol
  address: string;          // Token contract address
  count: number;            // Number of times bridged
  totalVolumeUSD: number;   // Total USD volume
  percentage: number;       // Percentage of total bridges
  logo?: string;            // Token logo URL
}

Token tracking

// Aggregate by token symbol (case-insensitive)
const tokenKey = tx.tokenSymbol.toUpperCase();
const tokenData = tokenCounts.get(tokenKey) || {
  count: 0,
  volumeUSD: 0,
  address: tx.tokenAddress,
};

tokenData.count++;
tokenData.volumeUSD += tx.amountUSD;
tokenCounts.set(tokenKey, tokenData);

Most bridged token

Bridge Wrapped identifies your most frequently bridged token:
// Find token with highest bridge count
for (const [symbol, data] of tokenCounts) {
  if (data.count > maxCount) {
    maxCount = data.count;
    maxSymbol = symbol;
    maxData = data;
  }
}

// Fetch logo from CoinMarketCap
const tokenInfo = await coinMarketCapService.getTokenInfo(maxData.address);

mostBridgedToken: {
  symbol: maxSymbol,
  address: maxData.address,
  count: maxData.count,
  totalVolumeUSD: maxData.volumeUSD,
  percentage: (maxData.count / total) * 100,
  logo: tokenInfo?.logo
}

Logo resolution

Token logos are sourced from multiple providers:
const SymbolToLogo: { [k: string]: string } = {
  BNB: 'https://cryptologos.cc/logos/bnb-bnb-logo.png?v=040',
  AVAX: 'https://cryptologos.cc/logos/avalanche-avax-logo.png?v=040',
  DAI: 'https://cryptologos.cc/logos/multi-collateral-dai-dai-logo.png?v=040',
  ETH: 'https://cryptologos.cc/logos/ethereum-eth-logo.png?v=040',
  USDC: 'https://coin-images.coingecko.com/coins/images/6319/large/usdc.png',
  USDT: 'https://coin-images.coingecko.com/coins/images/35023/large/USDT.png',
  WETH: 'https://coin-images.coingecko.com/coins/images/2518/large/weth.png',
  // ... additional mappings
};
Logos are loaded from external CDNs. The application gracefully handles missing logos by falling back to the token symbol display.

Batch token lookups

For efficiency, token information can be fetched in bulk:
async getMultipleTokenInfo(addresses: string[]): Promise<Map<string, TokenInfo>> {
  const results = new Map<string, TokenInfo>();
  
  for (const address of addresses) {
    const lowerAddress = address.toLowerCase();
    
    // Check cache
    const cached = this.cache.get(lowerAddress);
    if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
      results.set(lowerAddress, cached.data);
      continue;
    }
    
    // Get fallback info
    const fallback = this.getFallbackTokenInfo(address);
    if (fallback) {
      this.cache.set(lowerAddress, { data: fallback, timestamp: Date.now() });
      results.set(lowerAddress, fallback);
    }
  }
  
  return results;
}
This is used when calculating top tokens:
// Fetch all token logos in parallel
const addresses = sorted.map(([, data]) => data.address);
const tokenInfoMap = await coinMarketCapService.getMultipleTokenInfo(addresses);

return sorted.map(([symbol, data]) => ({
  symbol,
  address: data.address,
  count: data.count,
  totalVolumeUSD: data.volumeUSD,
  percentage: calculatePercentage(data.count, total),
  logo: tokenInfoMap.get(data.address.toLowerCase())?.logo,
}));

Unknown token handling

When a token cannot be identified, the service provides a safe fallback:
// Final fallback for truly unknown tokens
const unknownTokenInfo: TokenInfo = {
  symbol: address.slice(0, 6) + '...' + address.slice(-4),  // e.g., "0x1234...5678"
  name: 'Unknown Token',
  decimals: 18,  // Default to 18 decimals
};

this.cache.set(lowerAddress, { data: unknownTokenInfo, timestamp: Date.now() });
return unknownTokenInfo;
This ensures the UI always displays something meaningful, even for newly deployed or unrecognized tokens.

API integration details

Server-side vs client-side

The service adapts based on execution environment:
private isServer(): boolean {
  return typeof window === 'undefined';
}

private async fetchFromAPI(addresses: string[]): Promise<CoinMarketCapResponse | null> {
  if (this.isServer()) {
    // Server: Call CoinMarketCap directly with API key
    const apiKey = process.env.COINMARKETCAP_API_KEY;
    response = await fetch(
      `https://pro-api.coinmarketcap.com/v2/cryptocurrency/info?address=${addresses.join(',')}`,
      { headers: { 'X-CMC_PRO_API_KEY': apiKey } }
    );
  } else {
    // Client: Use internal API endpoint to hide key
    response = await fetch('/api/token-info', {
      method: 'POST',
      body: JSON.stringify({ addresses })
    });
  }
}

Request timeout

API requests are protected with a 5-second timeout:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

const response = await fetch(url, {
  signal: controller.signal,
});

clearTimeout(timeoutId);

Common token addresses

The types.ts file also defines a minimal set of common tokens for quick lookups:
export const COMMON_TOKENS: Record<string, TokenInfo> = {
  '0x0000000000000000000000000000000000000000': {
    symbol: 'ETH',
    address: '0x0000000000000000000000000000000000000000',
    decimals: 18,
  },
  '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2': {
    symbol: 'WETH',
    address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
    decimals: 18,
  },
  // ... USDC, USDT, DAI
};
This provides a lightweight fallback before querying external services. Located in src/services/bridges/types.ts:30.

Build docs developers (and LLMs) love