diff --git a/package.json b/package.json index e0876d2..e62a5a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tailwind-plus-icpay", - "version": "1.2.26", + "version": "1.2.27", "private": true, "packageManager": "pnpm@9.12.3", "scripts": { diff --git a/src/app/ledgers/page.mdx b/src/app/ledgers/page.mdx index d90b4cb..be09087 100644 --- a/src/app/ledgers/page.mdx +++ b/src/app/ledgers/page.mdx @@ -1,3 +1,34 @@ +import LedgersTable from '@/components/LedgersTable' + +export const metadata = { + title: 'Tokens (Shortcodes)', + description: + 'Browse supported tokens by chain, with their shortcode and contract/canister address. Prefer token shortcodes (e.g., ic_icp, base_usdcoin) when creating payments.', +} + +export const sections = [ + { title: 'Overview', id: 'overview' }, + { title: 'Token list', id: 'token-list' }, +] + +# Tokens (Shortcodes) + +Shortcodes are the primary way to reference tokens in ICPay. Each verified token ledger exposes a unique, human‑readable `shortcode` (for example, `ic_icp`, `base_usdc`). Use these in SDK and Widget calls instead of raw symbols, canister IDs, or contract addresses where possible. {{ className: 'lead' }} + +> Tip: You can also explore token availability via the Widget Builder, but this page provides a quick, scannable reference. + +## Overview + +- **Shortcode**: Canonical identifier used across ICPay SDK and Widgets. +- **Contract / Canister**: Token contract (EVM/Solana) or canister (IC). +- **Verified**: Indicates whether the token is verified in ICPay. + +This list updates periodically and is grouped by chain. If you don’t see a token you expect, check back later or use the Widget Builder to search interactively. + +## Token list + + + export const metadata = { title: 'Tokens', description: diff --git a/src/components/LedgersTable.tsx b/src/components/LedgersTable.tsx new file mode 100644 index 0000000..77b87eb --- /dev/null +++ b/src/components/LedgersTable.tsx @@ -0,0 +1,151 @@ +import React from 'react' + +type LedgerRow = { + id: string + name: string + symbol: string + shortcode?: string | null + canisterId: string + decimals: number + verified: boolean + // Chain fields (may come from either ledger or join) + chainName?: string | null + chainNameFromChain?: string | null + chainType?: string | null +} + +function getApiBaseUrl(): string { + // Prefer explicit docs env var if present; fallback to public API + const fromEnv = + process.env.NEXT_PUBLIC_API_URL || + process.env.ICPAY_API_URL || + process.env.NEXT_PUBLIC_ICPAY_API_URL + return (fromEnv && fromEnv.trim()) || 'https://api.icpay.org' +} + +async function fetchLedgers(): Promise { + const apiUrl = getApiBaseUrl() + const res = await fetch(`${apiUrl}/ledgers`, { + // Refresh approximately hourly; tokens do not change often + next: { revalidate: 60 * 60 }, + // Ensure server-side fetch + cache: 'force-cache', + }) + if (!res.ok) { + throw new Error(`Failed to load ledgers (${res.status})`) + } + const data = (await res.json()) as any[] + return data as LedgerRow[] +} + +function groupByChain(ledgers: LedgerRow[]): Map { + const groups = new Map() + for (const l of ledgers) { + const chainName = (l.chainNameFromChain || l.chainName || 'Unknown').toString() + const group = groups.get(chainName) + if (group) { + group.items.push(l) + } else { + groups.set(chainName, { chainType: l.chainType ?? null, items: [l] }) + } + } + // Sort items within each group by symbol then name for stable output + for (const [, g] of groups) { + g.items.sort((a, b) => { + const as = (a.symbol || '').toLowerCase() + const bs = (b.symbol || '').toLowerCase() + if (as !== bs) return as.localeCompare(bs) + return (a.name || '').toLowerCase().localeCompare((b.name || '').toLowerCase()) + }) + } + return groups +} + +export default async function LedgersTable() { + let ledgers: LedgerRow[] = [] + try { + ledgers = await fetchLedgers() + } catch (e: any) { + return ( +
+
Failed to load tokens
+
+ {(e && (e.message || String(e))) || 'An unexpected error occurred while fetching ledgers.'} +
+
+ ) + } + + if (!Array.isArray(ledgers) || ledgers.length === 0) { + return ( +
+ No tokens available at the moment. +
+ ) + } + + const groups = groupByChain(ledgers) + const chainNames = Array.from(groups.keys()).sort((a, b) => a.localeCompare(b)) + + return ( +
+ {chainNames.map((chainName) => { + const group = groups.get(chainName)! + const chainType = group.chainType ? group.chainType.toUpperCase() : null + return ( +
+

+ {chainName} + {chainType ? {chainType} : null} +

+
+ + + + + + + + + + + + {group.items.map((l) => { + const verifiedBadge = l.verified ? ( + + Yes + + ) : ( + + No + + ) + return ( + + + + + + + + ) + })} + +
TokenShortcodeContract / CanisterDecimalsVerified
+
{l.symbol}
+
{l.name}
+
+ {l.shortcode || '—'} + +
+ {l.canisterId} +
+
{l.decimals}{verifiedBadge}
+
+
+ ) + })} +
+ ) +} +