import { Hono } from 'hono' import * as z from 'zod/mini' import { base, mainnet } from 'viem/chains' import { tempo } from 'viem/tempo/chains' import * as Db from '../../db/Db.js' import * as OpenApi from '../../internal/OpenApi.js' import * as RoutesCatalog from '../../internal/routes/Catalog.js' import type * as App from '../App.js' /** Zod schemas owned by the admin route support matrix. */ export namespace schema { /** Schemas for the getAdminRoutesSupport operation. */ export namespace getRoutesSupport { const Chain = z .object({ id: z .string() .check(z.describe('CAIP-2 chain identifier.'), z.meta({ examples: ['eip155:1'] })), name: z .string() .check(z.describe('Human-readable chain name.'), z.meta({ examples: ['Ethereum'] })), slug: z .string() .check(z.describe('Stable chain slug.'), z.meta({ examples: ['ethereum'] })), }) .check(z.describe('Chain shown in the route support matrix.')) const Token = z .object({ id: z.string().check(z.describe('Stable route token id.'), z.meta({ examples: ['usdc'] })), name: z .string() .check(z.describe('Human-readable token name.'), z.meta({ examples: ['USD Coin'] })), symbol: z .string() .check(z.describe('Short token ticker symbol.'), z.meta({ examples: ['USDC'] })), }) .check(z.describe('Token shown in the route support matrix.')) const WalletTransferProvider = z .object({ modes: z .array(z.enum(['exactDestination', 'exactSource'])) .check( z.describe('Amount modes enabled for wallet transfers.'), z.meta({ examples: [['exactSource']] }), ), providerId: z .string() .check(z.describe('Stable route provider id.'), z.meta({ examples: ['stargate'] })), }) .check(z.describe('Provider with wallet-transfer support for the route.')) const Subsidies = z .object({ depositAddress: z .boolean() .check( z.describe('Whether the route is eligible for a conditional deposit subsidy.'), z.meta({ examples: [true] }), ), walletTransfer: z .boolean() .check( z.describe('Whether the route is eligible for a conditional wallet-transfer subsidy.'), z.meta({ examples: [false] }), ), }) .check(z.describe('Route-level subsidy eligibility before organization and amount checks.')) const Route = z .object({ depositAddressProviders: z .array(z.string()) .check( z.describe('Providers with deposit-address support for the route.'), z.meta({ examples: [['relay']] }), ), destinationChain: Chain, destinationToken: Token, id: z .string() .check( z.describe('Stable source-to-destination route id.'), z.meta({ examples: ['eip155:1/usdc-eip155:4217/usdce'] }), ), quoteProviders: z .array(z.string()) .check( z.describe('Providers with quote support for the route.'), z.meta({ examples: [['relay', 'stargate']] }), ), sourceChain: Chain, sourceToken: Token, subsidies: Subsidies, walletTransferProviders: z .array(WalletTransferProvider) .check( z.describe('Providers with wallet-transfer support for the route.'), z.meta({ examples: [[{ modes: ['exactSource'], providerId: 'stargate' }]] }), ), }) .check(z.describe('One source-to-destination route and its published support.')) /** Response body for the route support matrix. */ export const Response = z .object({ data: z.array(Route).check(z.describe('Published route support matrix.')), updatedAt: z .string() .check( z.describe('ISO timestamp of the catalog publish.'), z.meta({ examples: ['2026-09-14T12:00:00.000Z'] }), ), }) .check(z.describe('Published route support matrix.')) } } /** Read-only matrix of published route capabilities. */ export function routesSupport() { return new Hono().get( '/', OpenApi.describeRoute({ operationId: 'getAdminRoutesSupport', responses: OpenApi.responses({ success: { description: 'Published route support matrix.', schema: schema.getRoutesSupport.Response, }, }), summary: 'List route support', tags: ['Routes'], }), async (c) => { const catalog = await RoutesCatalog.read(Db.get(c.get('db'))) return c.json({ data: summarize(catalog.routes), updatedAt: catalog.updatedAt, }) }, ) } function summarize(routes: readonly RoutesCatalog.ProviderRoute[]) { type Row = { depositAddressProviders: string[] destinationChain: { id: string; name: string; slug: string } destinationToken: { id: string; name: string; symbol: string } id: string quoteProviders: string[] sourceChain: { id: string; name: string; slug: string } sourceToken: { id: string; name: string; symbol: string } subsidies: { depositAddress: boolean; walletTransfer: boolean } walletTransferProviders: { modes: ('exactDestination' | 'exactSource')[]; providerId: string }[] } const rows = new Map() for (const providerRoute of routes) { const { capabilities, providerId, route } = providerRoute const id = `${route.source.chain.id}/${route.source.slug}-${route.destination.chain.id}/${route.destination.slug}` const row = rows.get(id) ?? { depositAddressProviders: [], destinationChain: pickChain(route.destination.chain), destinationToken: pickToken(route.destination), id, quoteProviders: [], sourceChain: pickChain(route.source.chain), sourceToken: pickToken(route.source), subsidies: { depositAddress: false, walletTransfer: false }, walletTransferProviders: [], } row.quoteProviders.push(providerId) const usdPair = route.destination.currency === 'USD' && route.source.currency === route.destination.currency if (capabilities?.depositAddress) { row.depositAddressProviders.push(providerId) row.subsidies.depositAddress ||= route.destination.chain.id === `eip155:${tempo.id}` && usdPair } if (capabilities?.transfer) { row.walletTransferProviders.push({ modes: [...capabilities.transfer.modes], providerId }) row.subsidies.walletTransfer ||= capabilities.transfer.modes.includes('exactSource') && route.source.chain.id === `eip155:${tempo.id}` && [`eip155:${base.id}`, `eip155:${mainnet.id}`].includes(route.destination.chain.id) && usdPair } rows.set(id, row) } return [...rows.values()] } function pickChain(chain: RoutesCatalog.ProviderRoute['route']['source']['chain']) { return { id: chain.id, name: chain.name, slug: chain.slug } } function pickToken(token: RoutesCatalog.ProviderRoute['route']['source']) { return { id: token.slug, name: token.name, symbol: token.symbol } }