Skip to content

Add swaps.xyz swap provider - #480

Open
j0ntz wants to merge 6 commits into
masterfrom
jon/swapsxyz-integrate
Open

Add swaps.xyz swap provider#480
j0ntz wants to merge 6 commits into
masterfrom
jon/swapsxyz-integrate

Conversation

@j0ntz

@j0ntz j0ntz commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Description

Integrate swaps.xyz (a MoonPay cross-chain swap aggregator) as a new swap provider.

swaps.xyz exposes a REST API (https://api-v2.swaps.xyz/api, x-api-key auth). getPaths answers whether a pair is routable and with what amount limits; getAction returns the payload that executes it. It is classified as a CENTRALIZED provider (isDex: false, src/swap/central/swapsxyz.ts): every executable payload carries a swaps.xyz server signature from their fee module, and their alt-vm bridges have raised KYC flags, so the venue is server-gated. The Edge DEX litmus asks whether the venue can refuse you, not whether defi appears in the implementation.

One plugin, one pluginId, absorbing all route types (precedent: rango, thorchain). getAction's payload shape depends on the SOURCE chain's vmId, and fetchSwapQuoteInner dispatches on it:

vmId Payload Execution
evm {to, data, value, chainId} router calldata, plus an ERC20 approval pre-tx when requiresTokenApproval is set. data is '0x' on a route into a non-EVM destination, which is a plain value send to the bridge and gets no memo
solana {base64Tx, recentBlockhash, payer} unsigned v0 VersionedTransaction handed to SolanaEngine through spendInfo.otherParams.unsignedTx, as rango and lifi do
alt-vm {to, toExtra, value, chainId} deposit address on the source chain; toExtra becomes the chain's memo/tag when the route supplies one (XRP destination tag, XLM memo)

hypercore is rejected with SwapCurrencyError (Edge ships no currency plugin for it), as is any non-DEFAULT executionsType.

Registration. requiresRegisterTransaction comes back true on routes swaps.xyz does not broadcast itself; their docs make it mandatory for non-EVM transactions. makeSwapPluginQuote has no post-broadcast hook, so the plugin wraps the returned quote's approve and POSTs {txId, txHash} to /registerTxs once the hash exists. A registration failure is logged and swallowed: the swap is already on chain, and throwing would report a successful swap as failed.

Limits. getPaths returns minAmount/maxAmount as DECIMAL strings on the source token, not the base units the API reference claims. They are scaled by srcToken.decimals before any comparison (a floor rounds up, a ceiling rounds down, so a sub-unit residue on a low-decimal token can never widen the allowed range). A max request clamps to the route ceiling instead of failing.

Files:

  • src/swap/central/swapsxyz.tsmakeSwapsXyzPlugin. Resolves chain ids and token addresses (native = zero address on every VM), calls getPaths then getAction, classifies errors (unsupported chain/token/route and an unpayable destination address → SwapCurrencyError, below min / zero output → SwapBelowLimitError, over max → SwapAboveLimitError, unrecognized codes stay a plain Error so the core does not mis-rank them), and builds the spend per route model. A pure, exported makeSwapsXyzSpendInfo does the response → EdgeSpendInfo translation for unit testing.
  • src/mappings/swapsxyz.tsEdgeCurrencyPluginId → numeric chainId, covering the EVM chains plus the 26 non-EVM chains both Edge and swaps.xyz ship, each cross-checked against the live GET /getChainList. vmId disambiguates ids that belong to an EVM sibling: 314 is tagged alt-vm and a live getAction returns an f1… deposit address, so it is native Filecoin, not Filecoin FEVM (this corrects the earlier filecoinfevm → 314 entry).
  • src/index.ts — registers swapsxyz.
  • test/swapsxyz.test.ts — unit tests for the pure spend-info construction per route model (EVM native/token, empty calldata, solana native/token, alt-vm with and without a memo) plus end-to-end fetchSwapQuote coverage: the pre-network guards, the getPaths limit pre-check including decimal scaling, the getAction error classification into each typed swap error, the success-response guards, the registration callback firing only when required, and happy-path quotes for EVM and alt-vm sources. Driven with a faked io.fetchCors and faked wallets, following test/nym.test.ts.
  • AGENTS.md — a top-level orientation for coding agents on this repo: what the package is and why, the exact verification commands and non-obvious tooling (nyc supplies the sucrase/register hook, so plain npx mocha cannot load TypeScript), the central-vs-defi split and why it is about the venue rather than the payload, the fetchSwapQuote pipeline, the typed-error ranking contract, and an index into docs/. Written against the agents-md guidance that lives in the agent orchestration tooling: kept short because the file enters every session for this repo, with the directory listing, pasted snippets, and lint-enforced style rules deliberately left out.

Runtime key comes from the GUI env as SWAPSXYZ_INIT.apiKey (wiring PR: EdgeApp/edge-react-gui#6128).

Verification: npm run verify passes (prepare + lint + types + test). Exercised against the live swaps.xyz API end-to-end, and driven in-app on the iOS sim to a real executed swap for EACH route model — see the attached test-evidence screenshots (quote "Powered by swaps.xyz" → executed-swap success scene):

  • EVM source into an alt-vm destination (empty calldata): Base ETH → LTC
  • alt-vm source (deposit address + registration): LTC → Base USDC
  • solana source (unsigned transaction): SOL → Base USDC

Known provider constraint: swaps.xyz pays only t3… Zcash addresses, rejecting t1… and unified u1…, so Zcash destinations come back as INVALID_ADDRESS_FORMAT and are surfaced as SwapCurrencyError.

Asana: https://app.asana.com/0/1215088146871429/1217036054017879


Note

Cursor Bugbot is generating a summary for commit 151ec8e. Configure here.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@j0ntz

j0ntz commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (iOS sim — real swaps.xyz swap)

swapsxyz quote

swapsxyz quote

swapsxyz success

swapsxyz success

Captured by the agent's in-app test run (build-and-test).

@j0ntz
j0ntz marked this pull request as draft July 31, 2026 18:35
@j0ntz
j0ntz marked this pull request as ready for review July 31, 2026 18:35
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@j0ntz

j0ntz commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 9541abd. Configure here.

Comment thread src/mappings/swapsxyz.ts
Comment thread src/swap/central/swapsxyz.ts
@j0ntz
j0ntz force-pushed the jon/swapsxyz-integrate branch from 9541abd to c5ba82e Compare August 3, 2026 23:19
Comment thread src/swap/central/swapsxyz.ts
@j0ntz

j0ntz commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (after review fixes): swaps.xyz swap executed on the iOS sim

swapsxyz quote

swapsxyz quote

swap success

swap success

tx details

tx details

tx list row

tx list row

Captured by the agent's in-app test run (build-and-test).

@j0ntz
j0ntz force-pushed the jon/swapsxyz-integrate branch from c5ba82e to e1fae89 Compare August 3, 2026 23:42
Comment thread src/swap/defi/swapsxyz.ts Outdated
Comment thread src/swap/central/swapsxyz.ts
Comment thread src/swap/central/swapsxyz.ts
@j0ntz
j0ntz force-pushed the jon/swapsxyz-integrate branch from e1fae89 to 97ab5c4 Compare August 4, 2026 00:01
Comment thread src/swap/central/swapsxyz.ts Outdated
@j0ntz
j0ntz force-pushed the jon/swapsxyz-integrate branch from 97ab5c4 to 731100b Compare August 4, 2026 00:15
Integrate swaps.xyz (a MoonPay cross-chain DEX/bridge aggregator) as a new
DEX swap plugin. The provider's getAction endpoint returns executable EVM
calldata plus amountOut/amountOutMin and a token-approval flag, so the plugin
builds an EVM spend (calldata memo + optional ERC20 approval pre-tx) modeled on
the existing lifi plugin. Supports the EVM chains swaps.xyz confirms live;
non-EVM VMs and unmapped chains are rejected with SwapCurrencyError.
@j0ntz
j0ntz force-pushed the jon/swapsxyz-integrate branch 2 times, most recently from c19f45c to 2414049 Compare August 4, 2026 18:32
@j0ntz

j0ntz commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (decimal-limit fix, limit-publishing route)

sonic quote decimal limits

sonic quote decimal limits

sonic swap success

sonic swap success

Captured by the agent's in-app test run (build-and-test).

@j0ntz
j0ntz force-pushed the jon/swapsxyz-integrate branch from 2414049 to 92f4d7b Compare August 5, 2026 01:21
@j0ntz

j0ntz commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence: one in-app swap per route model (CEX reclass + solana/alt-vm dispatch)

evm to altvm quote

evm to altvm quote

evm to altvm success

evm to altvm success

altvm source quote

altvm source quote

altvm source success

altvm source success

solana quote

solana quote

solana success

solana success

Captured by the agent's in-app test run (build-and-test).

Comment thread src/swap/central/swapsxyz.ts
Comment thread src/swap/central/swapsxyz.ts
Comment thread src/swap/central/swapsxyz.ts Outdated
Comment thread src/swap/central/swapsxyz.ts
j0ntz added 2 commits August 4, 2026 18:31
Cover the plugin's typed swap errors end to end: drive fetchSwapQuote with a
faked io and wallets and assert SwapCurrencyError, SwapBelowLimitError and
SwapAboveLimitError for the guard, getAction-error and success-response paths,
plus one happy-path quote and a check that an unrecognized error code stays a
plain Error rather than a mis-ranked swap error.
j0ntz added 3 commits August 4, 2026 18:31
Top-level orientation for coding agents: build/test/verify commands, the
central-vs-defi plugin split, the fetchSwapQuote pipeline, the typed-error
ranking contract (and when a plain Error is correct), chain mappings, the
pure-and-end-to-end testing patterns, and pointers to the docs/ guides and
the .cursor/agents review sub-agents.
@j0ntz
j0ntz force-pushed the jon/swapsxyz-integrate branch from 92f4d7b to 151ec8e Compare August 5, 2026 01:40
@j0ntz

j0ntz commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (after review fixes)

retest after review fixes

retest after review fixes

Captured by the agent's in-app test run (build-and-test).

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 151ec8e. Configure here.

Comment thread src/mappings/swapsxyz.ts
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.

2 participants