List routed EVM tokens in supported cryptocurrencies - #1363
Merged
Conversation
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.
✅ Deploy Preview for vrtx-dashboard canceled.
|
❌ Deploy Preview for vortex-sandbox failed. Why did it fail? →
|
✅ Deploy Preview for vortexfi canceled.
|
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-cryptocurrencieslists 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), addsrampTypesper entry, and requires thenetworkquery parameter (400 without it). Routed lists are large (about 1,000 tokens on Base), so an unfiltered response no longer makes sense.rampTypesis derived from the same guards the quote service applies: networks outsideSUPPORTED_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
EvmTokenenum and config, soSELL PAXGreturned "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
networknow 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) andramp-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 againstvalidateChainSupportand the AssetHub retirement guardcatalog-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 rejectedbrl-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 confignetwork, 400 withoutapps/apisuite (1623 pass),bun typecheck,bun lint,bun run docs:api:check,bun run wire-contract:checkGET /v1/supported-cryptocurrencies?network=ethereumlists PAXG withrampTypes: ["BUY","SELL"];POST /v1/quotesSELL ETH→BRL and SELL PAXG→BRL return quotes