Add Swapter swap provider#475
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 59a7a92508
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
59a7a92 to
1ad9e59
Compare
1ad9e59 to
2110233
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2110233. Configure here.
Recreates partner PR #455 (author markovo4) on an EdgeApp branch, rebased onto master, with the integration brought to current plugin conventions: - Route quotes through getMaxSwappable so max swaps resolve, using a quote-only probe order that prices fees against the user's own refund address rather than creating an order that would be abandoned. - Drop the estimate round trip. Its response was fetched and discarded; create returns the authoritative amounts and the probe only needs a spendInfo to price fees. - Correct nine chain mappings against the live /data/coins network list. Swapter has renamed several networks since the partner branch was cut (ARBITRUM to ARB, OPTIMISM to OP, POLYGON to POL, SOLANA to SOL, ETHW to 'ETHEREUM POW'), which silently broke those chains. Bitcoin SV, Coreum, FIO and Telos are no longer offered in a form Edge can use, so they map to null. - Regenerate the partnerJson fixture from /data/coins so it reflects Swapter's real network identifiers and stays a subset of the mapping. The partner branch also added a self-referential edge-exchange-plugins dependency and a TypeScript 6 upgrade; both are dropped as unrelated.
2110233 to
5235c38
Compare

CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Description
Asana task
Adds the Swapter centralized swap provider.
Recreates partner PR #455 (by @markovo4, from
markovo4/edge-exchange-plugins) on anEdgeApp branch so it can run CI, rebased from its
v2.46.0merge-base onto currentmaster. The plugin design, mapping and test cases are the partner's work; this branchbrings the integration up to current conventions and fixes what had gone stale.
Asana: https://app.asana.com/1/9976422036640/project/1213880789473005/task/1216571782597915
Changes on top of the partner branch
Chain mappings corrected against the live
/data/coinslist. Swapter has renamedseveral networks since the partner branch was cut in May, and nine of the 43 non-null
mappings no longer resolve:
arbitrumARBITRUMARBoptimismOPTIMISMOPpolygonPOLYGONPOLsolanaSOLANASOLethereumpowETHWETHEREUM POW(note the space)bitcoinsvBSVcoreumCOREfioFIOtelosTELOSTELOS(EVM); Edge'stelosis EOSIO)A stale code here is caught by no type and no test: a mainnet quote still reaches the API
and fails with Swapter's
"Deposit coin and network combination does not exists.", whilea token quote throws
SwapCurrencyErrorbecause/data/coinsreturns no ticker set forthe unknown network. Verified by driving the plugin against the live API —
polygonatPOLYGONfailsmin-amountwith HTTP 400, and atPOLreturns HTTP 200. Bitcoin SV isdropped rather than remapped to Swapter's
BCHSV:getChainAndTokenCodesderives themainnet ticker from
currencyInfo.currencyCode(BSV), so that pair needs aSPECIAL_MAINNET_CASESentry to work at all.Max swaps now resolve. Every other central plugin routes its quote through
getMaxSwappable; this one did not, so itsquoteFor !== 'from'guard threwSwapCurrencyErrorfor every max ("send all") swap and Swapter silently vanished from theprovider list. It now follows the
nympattern: a quote-only probe order prices feesagainst the user's own refund address, so a max swap does not create an order that is
immediately abandoned, and exactly one order is created per quote.
Dropped the estimate round trip.
/v2/swap/estimatewas called and cleaned on everyquote, but its response was only logged —
toNativeAmountcomes from thecreateresponse. The probe needs only a
spendInfoto price fees, andcreatereturns theauthoritative amounts, so neither path needs it.
Regenerated
test/partnerJson/swapterMap.jsonfrom/data/coins. The partner fixtureheld 292 networks, 156 of which are not Swapter networks at all (
ERC20,ARBITRUMONE,AVAXCCHAIN, and a bareA), while omitting 72 real ones —POLwas absent entirely.Every other fixture in
test/partnerJson/is a subset of its provider's mapped codes; thisone now is too (38 networks, 729 tickers).
Dropped unrelated dependency churn: a self-referential
"edge-exchange-plugins": "^2.46.0"dependency, a
typescript~4.9.5→^6.0.3upgrade, a newts-nodedevDependency, andtheir
yarn.lockfallout. This branch touches no dependency files.Also registered
swapteralphabetically insrc/index.tsand dropped the whitespace-onlyedits to
partnerJson.test.ts.Testing
verify-repo.shpasses (prepare, eslint,tsc, mocha — 18 partnerJson cases including 3new Swapter ones, asserting against the regenerated live fixture).
An executed in-app swap is not part of this verification, and the plugin is unproven past
order creation. The throwaway key on the Asana task is not authorized:
/v2/swap/createand
/v2/swap/estimatereturn HTTP 401 under every documented auth variant, and/data/coins401s whenever anX-API-KEYheader is present at all. (/v2/swap/min-amountis unauthenticated, which is what made the mapping verification above possible.)
Driving the real plugin against the live API with that key gives:
So request construction and the mapping are confirmed end-to-end, and auth is the only
thing between this and an executed swap. A valid Swapter partner key is needed to
sign this off; until then the
createresponse cleaner (asSwapterCreateResponse) hasnever seen a real payload and is trusted from the published docs alone.
Follow-ups
asSwapterCreateResponseagainst a livecreateresponse.mapctlsynchronizer for Swapter./data/coinsneeds no API key, so it is aclean fit, and it would catch renames like
POLYGON→POLautomatically instead ofsilently dropping a chain. Not done here:
mapctl update-mappingsconstructs everyprovider's synchronizer eagerly and aborts without all of their API keys, so the
generated-mapping path could not be run or validated in this environment.
swapteris registered but is not yet enabled anywhere — the info server serves noSwapter init options, so the GUI needs
SWAPTER_INITbefore it can surface.Note
Medium Risk
New third-party swap path that builds real deposit spends and orders; behavior follows existing central plugins but live
createresponses have not been verified with a production API key.Overview
Adds Swapter as a new centralized swap provider and registers it in the plugin index.
The plugin quotes via Swapter’s
/v2/swap/min-amountand/v2/swap/createAPIs (API key required), hydrates supported tokens from public/data/coinswithout anX-API-KEYheader, and maps Edge chains to Swapter network codes insrc/mappings/swapter.ts. Quotes are deposit-amount only (quoteFor: 'from'); float vs fixed comes from user settings. Max (“send all”) swaps use the samegetMaxSwappable+ fee probe pattern as NYM so fee estimation does not create a stray order before the realcreatecall. Unsupported pairs (API error codes 1/2) surface asSwapCurrencyError; deposit memos accept numeric or string tags.Also adds
SWAPTER_INITin test config, regeneratestest/partnerJson/swapterMap.json, extendspartnerJson.testwith Swapter cases, and notes the provider in CHANGELOG.Reviewed by Cursor Bugbot for commit 5235c38. Bugbot is set up for automated code reviews on this repo. Configure here.