> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trustware.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Supported chains and assets

> Browse the source chains and assets Trustware can accept deposits from, and search them by name, ID, or address.

export const TokenPanel = ({chain, entry, tokenQuery, setTokenQuery, onRetry, panelClass, inputClass}) => {
  const tokenDisplayCap = 100;
  const matchesToken = (value, query) => (value || '').toLowerCase().includes(query);
  const header = <div className="flex items-center gap-3 px-3 py-2 border-b border-zinc-950/10 dark:border-white/10">
      <AssetImage src={chain.icon} seed={chain.chainId} label={chain.name} size="h-7 w-7 text-[10px]" />
      <span className="text-sm font-medium text-zinc-900 dark:text-zinc-50">{chain.name}</span>
      <span className="text-xs text-zinc-500">assets</span>
    </div>;
  if (!entry || entry.status === 'loading') {
    return <div className={panelClass}>
        {header}
        <p className="px-3 py-4 text-sm text-zinc-500">Loading assets…</p>
      </div>;
  }
  if (entry.status === 'error') {
    return <div className={panelClass}>
        {header}
        <div className="px-3 py-4">
          <p className="text-sm text-zinc-600 dark:text-zinc-300">
            The asset list for this chain could not be loaded.
          </p>
          <button type="button" onClick={onRetry} className="mt-2 text-sm font-medium text-blue-600 dark:text-blue-400">
            Retry
          </button>
        </div>
      </div>;
  }
  const query = tokenQuery.trim().toLowerCase();
  const matched = entry.tokens.filter(token => !query || matchesToken(token.symbol, query) || matchesToken(token.name, query) || matchesToken(token.address, query)).sort((a, b) => (a.symbol || '').localeCompare(b.symbol || ''));
  const shown = matched.slice(0, tokenDisplayCap);
  return <div className={panelClass}>
      {header}
      <div className="p-3 border-b border-zinc-950/10 dark:border-white/10">
        <input type="text" value={tokenQuery} onChange={event => setTokenQuery(event.target.value)} placeholder="Search assets by symbol, name, or address" aria-label="Search assets" className={inputClass} />
        <p className="mt-1 text-xs text-zinc-500">
          Showing {shown.length.toLocaleString()} of {matched.length.toLocaleString()}
          {entry.tokens.length !== matched.length ? ` (${entry.tokens.length.toLocaleString()} total)` : ''}
          {matched.length > tokenDisplayCap ? ' — refine your search to see more' : ''}
        </p>
      </div>
      <div className="max-h-96 overflow-y-auto divide-y divide-zinc-950/5 dark:divide-white/5">
        {shown.map(token => <div key={token.address} className="flex items-start gap-3 px-3 py-2">
            <AssetImage src={token.logoURI} seed={token.address} label={token.symbol} size="h-7 w-7 text-[10px] mt-0.5" />
            <div className="min-w-0 flex-1">
              <div className="flex items-baseline gap-2">
                <span className="text-sm font-medium text-zinc-900 dark:text-zinc-50">{token.symbol}</span>
                <span className="text-xs text-zinc-500 truncate">{token.name}</span>
              </div>
              <div className="text-xs font-mono text-zinc-500 break-all">{token.address}</div>
            </div>
            <span className="flex-shrink-0 text-xs text-zinc-400">{token.decimals} dec</span>
          </div>)}
        {matched.length === 0 && <p className="px-3 py-4 text-sm text-zinc-500">No assets match “{tokenQuery}”.</p>}
      </div>
    </div>;
};

export const SupportedChains = () => {
  const matchesChain = (value, query) => (value || '').toLowerCase().includes(query);
  const [chains, setChains] = useState(null);
  const [chainsFailed, setChainsFailed] = useState(false);
  const [chainQuery, setChainQuery] = useState('');
  const [selectedChainId, setSelectedChainId] = useState(null);
  const [tokenQuery, setTokenQuery] = useState('');
  const [tokenCache, setTokenCache] = useState({});
  const chainsRequest = useRef(null);
  const tokenRequests = useRef(new Map());
  const loadChains = () => {
    if (chainsRequest.current) chainsRequest.current.abort();
    const controller = new AbortController();
    chainsRequest.current = controller;
    setChains(null);
    setChainsFailed(false);
    fetch('/data/chains.json', {
      signal: controller.signal
    }).then(response => {
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      return response.json();
    }).then(data => {
      if (!controller.signal.aborted) {
        setChains(Array.isArray(data.chains) ? data.chains : []);
      }
    }).catch(error => {
      if (error.name !== 'AbortError' && !controller.signal.aborted) {
        setChainsFailed(true);
      }
    }).finally(() => {
      if (chainsRequest.current === controller) chainsRequest.current = null;
    });
  };
  useEffect(() => {
    loadChains();
    return () => {
      if (chainsRequest.current) chainsRequest.current.abort();
      for (const controller of tokenRequests.current.values()) controller.abort();
      tokenRequests.current.clear();
    };
  }, []);
  const selectChain = chainId => {
    setSelectedChainId(chainId);
    setTokenQuery('');
    const cached = tokenCache[chainId];
    if (cached?.status === 'loading' || cached?.status === 'ready') return;
    if (tokenRequests.current.has(chainId)) return;
    const controller = new AbortController();
    tokenRequests.current.set(chainId, controller);
    setTokenCache(prev => ({
      ...prev,
      [chainId]: {
        status: 'loading'
      }
    }));
    fetch(`/data/tokens/${chainId}.json`, {
      signal: controller.signal
    }).then(response => {
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      return response.json();
    }).then(data => {
      if (tokenRequests.current.get(chainId) !== controller) return;
      const tokens = Array.isArray(data.tokens) ? data.tokens : [];
      setTokenCache(prev => ({
        ...prev,
        [chainId]: {
          status: 'ready',
          tokens
        }
      }));
    }).catch(error => {
      if (error.name !== 'AbortError' && tokenRequests.current.get(chainId) === controller) {
        setTokenCache(prev => ({
          ...prev,
          [chainId]: {
            status: 'error'
          }
        }));
      }
    }).finally(() => {
      if (tokenRequests.current.get(chainId) === controller) {
        tokenRequests.current.delete(chainId);
      }
    });
  };
  const panelClass = 'rounded-xl border border-zinc-950/10 dark:border-white/10 overflow-hidden';
  const inputClass = 'w-full px-3 py-2 rounded-lg text-sm bg-zinc-950/[0.03] dark:bg-white/[0.06] ' + 'border border-zinc-950/10 dark:border-white/10 outline-none focus:border-blue-500';
  if (chainsFailed) {
    return <div className={`${panelClass} p-4 text-sm text-zinc-600 dark:text-zinc-300`}>
        <p>
          The supported-chains data could not be loaded. It is served as a static file on the live
          documentation site; a local preview may not serve <code>/data/chains.json</code>.
        </p>
        <button type="button" onClick={loadChains} className="mt-2 font-medium text-blue-600 dark:text-blue-400">
          Retry
        </button>
      </div>;
  }
  if (chains === null) {
    return <div className={`${panelClass} p-4 text-sm text-zinc-500`}>Loading supported chains…</div>;
  }
  const query = chainQuery.trim().toLowerCase();
  const visibleChains = chains.filter(chain => !query || matchesChain(chain.name, query) || matchesChain(String(chain.chainId), query) || matchesChain(chain.chainType, query) || matchesChain(chain.nativeCurrency && chain.nativeCurrency.symbol, query)).sort((a, b) => (a.name || '').localeCompare(b.name || ''));
  const selectedChain = selectedChainId ? chains.find(chain => chain.chainId === selectedChainId) : null;
  return <div className="flex flex-col gap-4 my-4">
      <div>
        <input type="text" value={chainQuery} onChange={event => setChainQuery(event.target.value)} placeholder="Search chains by name, ID, or type" aria-label="Search chains" className={inputClass} />
        <p className="mt-1 text-xs text-zinc-500">
          {visibleChains.length} of {chains.length} chains
        </p>
      </div>

      <div className={panelClass}>
        <div className="max-h-96 overflow-y-auto divide-y divide-zinc-950/5 dark:divide-white/5">
          {visibleChains.map(chain => {
    const isSelected = chain.chainId === selectedChainId;
    return <button key={chain.chainId} type="button" onClick={() => selectChain(chain.chainId)} aria-pressed={isSelected} className={`w-full flex items-center gap-3 px-3 py-2 text-left transition-colors ${isSelected ? 'bg-blue-500/10' : 'hover:bg-zinc-950/[0.03] dark:hover:bg-white/[0.05]'}`}>
                <AssetImage src={chain.icon} seed={chain.chainId} label={chain.name} size="h-8 w-8 text-xs" />
                <span className="min-w-0 flex-1">
                  <span className="block text-sm font-medium text-zinc-900 dark:text-zinc-50 truncate">
                    {chain.name}
                  </span>
                  <span className="block text-xs text-zinc-500">
                    ID {chain.chainId} · {chain.chainType}
                    {chain.nativeCurrency && chain.nativeCurrency.symbol ? ` · ${chain.nativeCurrency.symbol}` : ''}
                  </span>
                </span>
                <span className="flex-shrink-0 text-xs text-zinc-400">
                  {Number(chain.tokenCount || 0).toLocaleString()} tokens
                </span>
              </button>;
  })}
          {visibleChains.length === 0 && <p className="px-3 py-4 text-sm text-zinc-500">No chains match “{chainQuery}”.</p>}
        </div>
      </div>

      {selectedChain && <TokenPanel chain={selectedChain} entry={tokenCache[selectedChainId]} tokenQuery={tokenQuery} setTokenQuery={setTokenQuery} onRetry={() => selectChain(selectedChainId)} panelClass={panelClass} inputClass={inputClass} />}
    </div>;
};

export const Badge = ({seed, label, size}) => {
  let hash = 7;
  for (let i = 0; i < seed.length; i += 1) {
    hash = hash * 31 + seed.charCodeAt(i) >>> 0;
  }
  const cleaned = (label || '').replace(/[^a-zA-Z0-9]/g, '');
  const badgeLabel = cleaned ? cleaned.slice(0, 2).toUpperCase() : '?';
  return <span className={['flex-shrink-0 flex items-center justify-center', 'rounded-full font-semibold text-white', size].join(' ')} style={{
    backgroundColor: `hsl(${hash % 360} 55% 45%)`
  }} aria-hidden="true">
      {badgeLabel}
    </span>;
};

export const AssetImage = ({src, seed, label, size, alt}) => {
  const [failedSrc, setFailedSrc] = useState(null);
  if (!src || failedSrc === src) {
    return <Badge seed={seed} label={label} size={size} />;
  }
  return <img src={src} alt={alt || ''} aria-hidden={alt ? undefined : 'true'} loading="lazy" decoding="async" referrerPolicy="no-referrer" onError={() => setFailedSrc(src)} className={['flex-shrink-0 rounded-full object-contain bg-zinc-950/[0.03] dark:bg-white/10', size].join(' ')} />;
};

Trustware accepts deposits from thousands of assets across many source chains. Use the explorer below to find a chain, then browse or search its supported assets. Select a chain to load its asset list on demand.

<Note>
  This list is generated from Trustware discovery data, so counts can change as the dataset refreshes. It shows the source chains and assets a user can deposit from. The destination chain and token are whatever you configure in your integration.
</Note>

<SupportedChains />

Chain icons are served from this documentation site. Token logos load from public asset hosts, so they appear only where a logo is available and fall back to a lettered badge otherwise.

## Query these programmatically

The explorer above reads the same public data your app can consume at runtime, so you never need to hardcode supported chains or assets.

* **REST API**: call the [discovery endpoints](/api-reference/discovery) (`GET /chains` and `GET /tokens`) to populate selectors dynamically.
* **SDK**: use `Trustware.useChains()` and `Trustware.useTokens(chainId)` to fetch the same lists inside a React app.

The explorer is a generated snapshot. Use the REST API or SDK at runtime for the live set; an entry shown here reflects availability at the snapshot’s last refresh.
