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

# Payment Rail, Chain, & Token Support

export const SupportedTokensTable = () => {
  const PAGE_SIZE = 6;
  const ASSETS_URL = 'https://v2.prod.halliday.xyz/assets';
  const [rows, setRows] = useState(null);
  const [error, setError] = useState(null);
  const [filter, setFilter] = useState('');
  const [page, setPage] = useState(0);
  const [copiedId, setCopiedId] = useState(null);
  useEffect(() => {
    let active = true;
    fetch(ASSETS_URL).then(res => res.json()).then(data => {
      if (!active) return;
      const parsed = Object.entries(data).filter(([, asset]) => asset && asset.chain).map(([identifier, asset]) => ({
        name: asset.name || '',
        symbol: asset.symbol || '',
        identifier
      })).sort((a, b) => a.name.localeCompare(b.name));
      setRows(parsed);
    }).catch(err => {
      if (active) setError(err.message);
    });
    return () => {
      active = false;
    };
  }, []);
  useEffect(() => {
    setPage(0);
  }, [filter]);
  const filtered = useMemo(() => {
    if (!rows) return [];
    const query = filter.trim().toLowerCase();
    if (!query) return rows;
    return rows.filter(row => row.name.toLowerCase().includes(query) || row.symbol.toLowerCase().includes(query) || row.identifier.toLowerCase().includes(query));
  }, [rows, filter]);
  const pageCount = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
  const current = Math.min(page, pageCount - 1);
  const start = current * PAGE_SIZE;
  const visible = filtered.slice(start, start + PAGE_SIZE);
  const copyIdentifier = identifier => {
    navigator.clipboard.writeText(identifier).then(() => {
      setCopiedId(identifier);
      setTimeout(() => setCopiedId(c => c === identifier ? null : c), 1200);
    });
  };
  const clipboardIcon = <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
            <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
        </svg>;
  const checkIcon = <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M20 6 9 17l-5-5" />
        </svg>;
  return <div className="sttWrap">
            <style>{`
                .sttWrap { margin: 0; }
                .sttControls {
                    display: flex;
                    justify-content: flex-start;
                    gap: 8px;
                    align-items: center;
                    margin-bottom: 12px;
                    flex-wrap: wrap;
                }
                .sttInput {
                    flex: 0 1 280px;
                    min-width: 160px;
                    margin-left: 0;
                    padding: 6px 10px;
                    font-size: 14px;
                    color: inherit;
                    background: transparent;
                    border: 1px solid rgb(0 0 0 / 0.15);
                    border-radius: 6px;
                }
                :is(.dark) .sttInput { border-color: rgb(255 255 255 / 0.2); }
                .sttInput:focus {
                    outline: none;
                    border-color: rgb(var(--primary));
                }
                .sttReset {
                    padding: 6px 12px;
                    font-size: 14px;
                    color: inherit;
                    background: transparent;
                    border: 1px solid rgb(0 0 0 / 0.15);
                    border-radius: 6px;
                    cursor: pointer;
                }
                :is(.dark) .sttReset { border-color: rgb(255 255 255 / 0.2); }
                .sttReset:hover { border-color: rgb(var(--primary)); }
                .sttTableWrap {
                    border: 1px solid rgb(0 0 0 / 0.1);
                    border-radius: 8px;
                    overflow: hidden;
                }
                :is(.dark) .sttTableWrap { border-color: rgb(255 255 255 / 0.1); }
                .sttGrid {
                    display: grid;
                    grid-template-columns: minmax(0, 25fr) minmax(0, 25fr) minmax(0, 47fr);
                    grid-template-rows: auto;
                    grid-auto-rows: 90px;
                    font-size: 14px;
                }
                @media (max-width: 400px) {
                    .sttGrid { grid-auto-rows: 120px; }
                }
                .sttRow { display: contents; }
                .sttCell {
                    padding: 8px 12px;
                    min-width: 0;
                    overflow: hidden;
                    overflow-wrap: break-word;
                    border-bottom: 1px solid rgb(0 0 0 / 0.1);
                }
                :is(.dark) .sttCell { border-bottom-color: rgb(255 255 255 / 0.1); }
                .sttHeadCell { font-weight: 600; }
                .sttNoResults { grid-column: 1 / -1; }
                .sttIdCell {
                    display: flex;
                    align-items: flex-start;
                    justify-content: space-between;
                    gap: 8px;
                }
                .sttIdText {
                    min-width: 0;
                    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                    font-size: 12px;
                    word-break: break-all;
                }
                .sttCopy {
                    flex-shrink: 0;
                    display: inline-flex;
                    align-items: center;
                    justify-content: center;
                    padding: 4px;
                    color: inherit;
                    background: transparent;
                    border: 1px solid rgb(0 0 0 / 0.15);
                    border-radius: 6px;
                    cursor: pointer;
                }
                :is(.dark) .sttCopy { border-color: rgb(255 255 255 / 0.2); }
                .sttCopy:hover { border-color: rgb(var(--primary)); }
                .sttCopy svg { width: 14px; height: 14px; display: block; }
                .sttFooter {
                    display: flex;
                    gap: 12px;
                    align-items: center;
                    margin-top: 12px;
                    font-size: 14px;
                }
                .sttPageBtn {
                    padding: 4px 10px;
                    font-size: 14px;
                    color: inherit;
                    background: transparent;
                    border: 1px solid rgb(0 0 0 / 0.15);
                    border-radius: 6px;
                    cursor: pointer;
                }
                :is(.dark) .sttPageBtn { border-color: rgb(255 255 255 / 0.2); }
                .sttPageBtn:hover:not(:disabled) { border-color: rgb(var(--primary)); }
                .sttPageBtn:disabled { opacity: 0.4; cursor: default; }
                .sttMuted { opacity: 0.6; }
            `}</style>

            <div className="sttControls">
                <input className="sttInput" type="text" placeholder="Filter tokens…" value={filter} onChange={e => setFilter(e.target.value)} />
                <button className="sttReset" onClick={() => setFilter('')}>Reset Filter</button>
            </div>

            {error && <p className="sttMuted">Could not load tokens: {error}</p>}
            {!error && !rows && <p className="sttMuted">Loading supported tokens…</p>}

            {rows && <>
                    <div className="sttTableWrap">
                        <div className="sttGrid">
                            <div className="sttCell sttHeadCell">Token Name</div>
                            <div className="sttCell sttHeadCell">Token Symbol</div>
                            <div className="sttCell sttHeadCell">Identifier</div>

                            {visible.map(row => <div className="sttRow" key={row.identifier}>
                                    <div className="sttCell">{row.name}</div>
                                    <div className="sttCell">{row.symbol}</div>
                                    <div className="sttCell">
                                        <div className="sttIdCell">
                                            <span className="sttIdText">{row.identifier}</span>
                                            <button className="sttCopy" onClick={() => copyIdentifier(row.identifier)} aria-label="Copy identifier" title="Copy identifier">
                                                {copiedId === row.identifier ? checkIcon : clipboardIcon}
                                            </button>
                                        </div>
                                    </div>
                                </div>)}

                            {visible.length === 0 && <div className="sttCell sttNoResults sttMuted">No tokens match “{filter}”.</div>}
                        </div>
                    </div>

                    <div className="sttFooter">
                        <button className="sttPageBtn" onClick={() => setPage(p => Math.max(0, p - 1))} disabled={current === 0}>
                            Previous
                        </button>
                        <button className="sttPageBtn" onClick={() => setPage(p => Math.min(pageCount - 1, p + 1))} disabled={current >= pageCount - 1}>
                            Next
                        </button>
                        <span className="sttMuted">
                            {filtered.length === 0 ? '0 tokens' : `${start + 1}–${start + visible.length} of ${filtered.length}`}
                        </span>
                    </div>
                </>}
        </div>;
};

export const SupportedChainsTable = () => {
  const CHAINS_URL = 'https://v2.prod.halliday.xyz/chains';
  const [rows, setRows] = useState(null);
  const [error, setError] = useState(null);
  useEffect(() => {
    let active = true;
    fetch(CHAINS_URL).then(res => res.json()).then(data => {
      if (!active) return;
      const parsed = Object.values(data).filter(chain => chain && chain.network).map(chain => ({
        name: chain.network,
        chainId: chain.chain_id && chain.chain_id['#'] ? chain.chain_id['#'] : '',
        type: chain.address_family || '',
        explorer: chain.explorer || ''
      })).sort((a, b) => a.name.localeCompare(b.name));
      setRows(parsed);
    }).catch(err => {
      if (active) setError(err.message);
    });
    return () => {
      active = false;
    };
  }, []);
  const displayName = name => name.charAt(0).toUpperCase() + name.slice(1);
  const truncateChainId = chainId => {
    const id = String(chainId);
    return id.length > 10 ? id.slice(0, 10) + '…' : id;
  };
  if (error) return <p>Could not load chains: {error}</p>;
  if (!rows) return <p>Loading supported chains…</p>;
  return <div className="supported-chains-table">
            <style>{`
                .supported-chains-table table {
                    width: auto;
                    min-width: 0;
                    table-layout: auto;
                }
                .supported-chains-table th,
                .supported-chains-table td {
                    white-space: nowrap;
                }
                .supported-chains-table td a,
                .supported-chains-table td a:hover {
                    text-decoration: none;
                    border-bottom: none;
                }
            `}</style>
            <table>
            <thead>
                <tr>
                    <th>Chain Name</th>
                    <th>Chain ID</th>
                    <th>Type</th>
                    <th>Explorer</th>
                </tr>
            </thead>
            <tbody>
                {rows.map(row => <tr key={row.name}>
                        <td>{displayName(row.name)}</td>
                        <td title={row.chainId}>{truncateChainId(row.chainId)}</td>
                        <td>{row.type}</td>
                        <td>
                            {row.explorer && <a href={row.explorer} target="_blank" rel="noopener noreferrer" aria-label="Open block explorer" title="Open block explorer">
                                    <Icon icon="arrow-up-right-from-square" size={16} />
                                </a>}
                        </td>
                    </tr>)}
            </tbody>
            </table>
        </div>;
};

## Fiat on and offramps

Halliday supports fiat-to-crypto onramps and offramps worldwide.

Available payment rails depend on the payment providers Halliday integrates with, and each provider supports a different set of rails and regions.

As a result, not every rail is available in every location. In non-sanctioned regions, Halliday's supported fiat onramps generally include:

* Debit card
* Credit card
* Bank ACH deposit
* Apple Pay
* Google Pay
* PayPal
* Venmo
* Coinbase

For more detailed regional support, reach out to [partnerships@halliday.xyz](mailto:partnerships@halliday.xyz) or click the **Book a Sales Call** button above.

## Crypto Assets

Halliday supports crypto assets as input and output for payments. Supported crypto assets can be found by querying the `/assets` [endpoint of the API](/api-reference/assets/get-asset-details).

Get an API key for free in the [Halliday Dashboard](https://dashboard.halliday.xyz/).

Specific payment [route support from input asset to output asset](https://docs.halliday.xyz/api-reference/assets/get-available-input-assets-for-a-given-output-asset) can also be checked using the API.

Reach out to the team directly at [partnerships@halliday.xyz](mailto:partnerships@halliday.xyz) to request additional support for fiat currencies, tokens, or chains.

## Supported Chains

The following blockchains are supported by Halliday:

<SupportedChainsTable />

## Supported Tokens

All supported tokens are available as a destination token for fiat onramps and swaps.

<SupportedTokensTable />
