Skip to content

List routed EVM tokens in supported cryptocurrencies - #1363

Merged
ebma merged 8 commits into
stagingfrom
feat/supported-cryptocurrencies-routed-tokens
Sep 8, 2026
Merged

List routed EVM tokens in supported cryptocurrencies#1363
ebma merged 8 commits into
stagingfrom
feat/supported-cryptocurrencies-routed-tokens

Conversation

@ebma

@ebma ebma commented Sep 7, 2026

Copy link
Copy Markdown
Member

Summary

Third-party integrators (the PAXG pilot) could not discover which tokens the quote engine accepts, and selling any Squid-routed token, or any native token, failed at quote time. Three related fixes:

1. GET /v1/supported-cryptocurrencies lists routed tokens. The endpoint only read the static token config, so tokens the quote engine already accepts through the dynamic Squid Router list were never advertised. It now reads the merged dynamic config (deduping enum-key/symbol aliases by contract address), adds rampTypes per entry, and requires the network query parameter (400 without it). Routed lists are large (about 1,000 tokens on Base), so an unfiltered response no longer makes sense. rampTypes is derived from the same guards the quote service applies: networks outside SUPPORTED_CHAINS (Moonbeam, testnets) and AssetHub, whose only corridors are retired, get [].

2. SELL of routed tokens is restored (regression from the block-flow port). Before the port, the legacy route resolver sent any EVM source token to the offramp strategy and resolved it through the merged token catalog. The port gated every EVM SELL definition on the static EvmToken enum and config, so SELL PAXG returned "No block flow mapped". The SELL definitions now match any on-chain symbol structurally, as the BUY cross-chain definitions already do, and the flow input resolver rejects symbols unknown to the merged token catalog with a 400 at quote time. Matching deliberately does not consult live token discovery: startup re-resolves every pending quote and resumable ramp through these predicates and exits the process on failure, so a discovery-dependent predicate would let one routed-token ramp plus a failed Squid token fetch crash-loop the API. The execution path (Squid bridge to Base USDC, user-signed approve and swap) was already token-agnostic.

3. Native-token sells no longer collapse to a zero swap input. Squid sends a native source (ETH, POL) as msg.value, so the route's transaction value is the swapped principal plus the router fee. The network-fee helper priced the whole value as fee, fee distribution zeroed the bridged amount, and the Nabla stage rejected it with "Input amount for swap must be greater than 0" on production for every ETH and POL sell. The helper now subtracts the principal for native sources. The fee miscalculation predates the port, but the legacy pipeline never deducted the Squid network fee from the swap input, so it only became a hard failure after the port.

Breaking: callers of the supported-cryptocurrencies endpoint that omit network now get a 400. No callers inside the repo; the SDK does not wrap this endpoint.

Security spec updated: fee-integrity.md (native-source fee rule) and ramp-phase-flows.md (SELL source matching and discovery independence).

Test plan

  • cryptocurrencies.config.test.ts: routed and static tokens listed with dedupe, static fallback before the dynamic list loads, missing-network rejection, and [] for Moonbeam/testnets and AssetHub cross-checked against validateChainSupport and the AssetHub retirement guard
  • catalog-routed-offramp.test.ts: a routed source token resolves to the BRL, EUR, and Alfredpay SELL flows with no dynamic token list loaded; a fiat symbol is still rejected
  • brl-offramp-crosschain.scenario.test.ts: native ETH SELL quote succeeds with only the router fee as network fee; an unknown SELL source gets a 400 from the flow input; an active PAXG SELL ramp still passes the startup compatibility check after token discovery falls back to the static config
  • HTTP surface invariants: 200 with network, 400 without
  • Full apps/api suite (1623 pass), bun typecheck, bun lint, bun run docs:api:check, bun run wire-contract:check
  • After staging deploy: GET /v1/supported-cryptocurrencies?network=ethereum lists PAXG with rampTypes: ["BUY","SELL"]; POST /v1/quotes SELL ETH→BRL and SELL PAXG→BRL return quotes

ebma added 2 commits September 7, 2026 17:21
The endpoint only read the static token config, so tokens the quote
engine accepts via the dynamic Squid Router list (e.g. PAXG) were never
advertised. Read the merged dynamic config instead, dedupe the alias
keys, and expose rampTypes so integrators can see that routed tokens
are BUY-only. The network filter is now required to keep responses
per-network.
@netlify

netlify Bot commented Sep 7, 2026

Copy link
Copy Markdown

Deploy Preview for vrtx-dashboard canceled.

Name Link
🔨 Latest commit 794eaff
🔍 Latest deploy log https://app.netlify.com/projects/vrtx-dashboard/deploys/6a9fd144677fc500087f556b

@netlify

netlify Bot commented Sep 7, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox failed. Why did it fail? →

Name Link
🔨 Latest commit 794eaff
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a9fd144aabda600084bf5ed

@netlify

netlify Bot commented Sep 7, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi canceled.

Name Link
🔨 Latest commit 794eaff
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a9fd1443c163b0008944800

ebma added 6 commits September 8, 2026 10:11
Squid sends a native source token (ETH, POL) as msg.value, so the route's
transaction value carries the swapped principal on top of the router fee.
Pricing the whole value as network fee exceeded the bridged amount, the
fee distribution zeroed the swap input, and every native-token SELL quote
failed with "Input amount for swap must be greater than 0". Subtract the
principal for native sources so only the router fee is charged.
The block-flow port gated every EVM SELL definition on the static token
enum and config, while the legacy route resolver accepted any token the
Squid-discovered catalog knew. Resolve SELL sources through the same
merged token catalog BUY destinations use, restoring sells of routed
tokens such as PAXG.
With the SELL catalog resolving routed tokens, rampTypes no longer
depends on whether an EVM token comes from the static config.
Startup re-resolves every pending quote and resumable ramp through the
catalog's match predicates and exits the process on failure. Matching
SELL sources against live Squid discovery meant one routed-token ramp
plus a failed token fetch at boot would crash-loop the API. Match SELL
sources structurally, as BUY destinations already are, and let the flow
input resolver reject unknown symbols with a 400 at quote time.
rampTypes claimed BUY and SELL for Moonbeam, testnets, and AssetHub,
although quote creation rejects those networks or has retired their
only corridors. Derive the directions from the same guards the quote
service applies, naming the AssetHub retirement so both share it.
@ebma
ebma merged commit fbfa01c into staging Sep 8, 2026
5 of 6 checks passed
@ebma
ebma deleted the feat/supported-cryptocurrencies-routed-tokens branch September 8, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant