Add SimpleSwap swap support - #488
Open
j0ntz wants to merge 4 commits into
Open
Conversation
A fixed-rate quote that failed the range check never tried the float flow, whose limits can be wider, so users were blocked from a possible swap. Float is now attempted after any currency or limit error from the fixed flow; if the float flow lacks the pair entirely, the more actionable fixed-rate limit error is surfaced instead. Also types the catch clauses as unknown.
Pass the estimate's validUntil through ensureInFuture so a stale or clock-skewed timestamp can't produce an already-expired quote.
6 tasks
Contributor
Author
j0ntz
marked this pull request as ready for review
August 19, 2026 23:25
j0ntz
force-pushed
the
jon/simpleswap-swap-support
branch
from
August 19, 2026 23:25
a7891ec to
f3955f2
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f3955f2. Configure here.
j0ntz
force-pushed
the
jon/simpleswap-swap-support
branch
from
August 19, 2026 23:35
f3955f2 to
d0da4e9
Compare
Four defects found by driving the plugin against SimpleSwap's live API: - Fixed-rate order creation returns HTTP 500 "check extra_id" for any destination chain whose currency record carries hasExtraId (XRP, XLM and every other tag chain). Edge pays out to the user's own non-custodial wallet and has no destination tag to send, and an empty or null extraIdTo is rejected the same way, so the float flow is the only one those pairs can use. The retry now covers any fixed-flow failure rather than swap errors alone. - A server error from the float flow masked the fixed flow's clean SwapCurrencyError, so an unsupported pair surfaced as "SimpleSwap returned error code 500". Both errors are now ranked and the more actionable one is thrown. - denominationToNative results reached the core unrounded, so a provider amount carrying more decimals than the asset's denomination produced a fractional native amount. Minimums round up, maximums and receive amounts round down. - The provider-echoed amountFrom drove the spend with no bound against the requested amount. It is now bounded, and the failure is fatal rather than retryable so the created order is never abandoned for a second one.
j0ntz
force-pushed
the
jon/simpleswap-swap-support
branch
from
August 19, 2026 23:44
d0da4e9 to
f3c5315
Compare
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.








CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Description
Asana: https://app.asana.com/0/1215088146871429/1217205278669378
Adds SimpleSwap as a centralized swap provider. This is SimpleSwap's own
integration from #481,
recreated on an EdgeApp branch so it can land from here. Their three commits are
preserved with their authorship; the fourth commit is Edge's review pass.
Companion GUI PR: EdgeApp/edge-react-gui#6167.
What the plugin does
src/swap/central/simpleswap.tscalls SimpleSwap API v3 (ranges,estimates,exchanges) with anx-api-key. It prefers a fixed rate and falls back to afloat rate, checks the pair limits before creating an order, and maps API errors
onto the Edge swap error types. A
maxquote uses a local fee probe rather thancreating and abandoning a live order. Chain mappings and a mapctl synchronizer
come from SimpleSwap's live currency list, and
test/simpleswap.test.tscoversthe mapping for every
EdgeCurrencyPluginIdplus both quote directions.Init options:
SIMPLESWAP_INIT: { "apiKey": "..." }. The factory throws on anempty key, so builds without the key skip the plugin.
The Edge review pass (commit 4)
Seven defects. The first four came from driving the plugin against the live API;
the last three came from the reviewer bots on this PR. Each was re-verified
against the live API after the fix:
POST /v3/exchangeswithfixed: truereturns500 "check extra_id"whenever thedestination currency carries
hasExtraId(XRP, XLM, and every other tagchain). Edge pays out to the user's own non-custodial wallet and has no
destination tag to send, and an empty or null
extraIdTois rejected the sameway, so the float flow is the only one those pairs can use. The plugin raised
the 500 as a hard error and never retried, so every swap to those chains
failed. The retry now covers any fixed-flow failure, not swap errors alone.
SwapCurrencyError,so an unsupported pair surfaced as "SimpleSwap returned error code 500". Both
errors are ranked now and the more actionable one is thrown.
denominationToNativeresults reached the core unrounded. A live ATOMquote produced
toNativeAmount = 2346482116.51. Minimums round up, maximumsand receive amounts round down.
amountFromdrove the spend with no bound against therequested amount. Both directions are bounded now: a
fromquote requires theechoed source amount to equal the amount the order was created from (above it
overspends, below it underpays a live order), and a reverse quote rejects a
destination amount below the pinned receive amount.
extraIdFromcleaned throughasOptional(asString), so a JSON number tag (XRP, XLM, TON) failed the cleanerafter the order already existed, and tag
0was lost. It cleans throughasNumberStringnow, and an empty tag is treated as absent rather thanbecoming an empty memo.
POST /v3/exchangesreturns now runs inside a fatal boundary: the responsecleaner, the amount arithmetic, the bound checks and the spend construction
all raise a fatal error that
getQuoterethrows instead of retrying. Thefloat retry can only run on a failure that happened before the order existed.
Verification
npm run verifypasses; the SimpleSwap suite is 26 tests. Driven end to end onthe iOS simulator with every other swap provider disabled locally so the engine
had to route through SimpleSwap: an 80 XLM to XMR swap executed to the success
scene, and the Stellar send carried SimpleSwap's numeric deposit memo.
Screenshots below.
Known gaps, tracked separately
SimpleSwap is receive-only for Zano and Pirate Chain (no sell pair exists in any
direction), and Zcash payouts accept transparent and TEX addresses only, so
there is no unified-address withdrawal. Those are partner-side gaps under
discussion, not plugin defects; the plugin surfaces each as a clean
SwapCurrencyError.