Skip to content

Fix Xgram reverse quote amounts and address open review findings#464

Merged
j0ntz merged 2 commits into
masterfrom
jon/xgram-405-gap
Jun 23, 2026
Merged

Fix Xgram reverse quote amounts and address open review findings#464
j0ntz merged 2 commits into
masterfrom
jon/xgram-405-gap

Conversation

@j0ntz

@j0ntz j0ntz commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Description

Closes the gaps found while reviewing the merged #441 (the reapplied Xgram plugin work) against the original #405 and its automated review comments.

Asana: https://app.asana.com/1/9976422036640/project/1213880789473005/task/1210642564105749

Reverse quote amount mapping (functional change missed from #405)

#441 reapplied the Xgram plugin but did not carry over the reverse-quote fix the original author added in #405 (the "fixed amount from/to bug" commit). On master, createOrder mapped fromAmount and toAmount the same way for both quote directions. The reverse "to" quote uses the launch-new-payment-exchange-edge endpoint, whose ccyAmountFrom and ccyAmountToExpected fields carry the opposite sides from the forward endpoint.

Verified against the live Xgram API:

  • Forward (launch-new-exchange-edge), send 0.5 ETH for BTC: ccyAmountFrom=0.5 (the send amount), ccyAmountToExpected=0.01337 (the receive amount), deposit address on ETH.
  • Reverse (launch-new-payment-exchange-edge), receive 0.01 BTC from ETH: ccyAmountFrom=0.01 (the requested receive amount), ccyAmountToExpected=0.3769 (the computed send amount, about 0.377 ETH, which matches the forward rate), deposit address on ETH.

So for reverse quotes ccyAmountFrom is the receive side and ccyAmountToExpected is the send side. Master assigned them backwards, so any quote where the user enters the receive amount reported the send and receive amounts swapped (off by the whole exchange rate). This branches the mapping on isSelling, matching the original #405 fix.

Note on the Cursor Bugbot comment on #405: it flagged this same change as a High-severity bug, reasoning from the field names (downstream treats fromAmount as the send side). The live API confirms the reverse endpoint swaps those fields relative to their names, so the branched mapping is the correct one and that comment is a false positive.

Open automated-review findings from #405

  • Prefer the CORS-capable fetch (const { fetchCors = io.fetch } = io), matching every other central swap plugin. Master used io.fetch directly, which can fail cross-origin quote requests in browser contexts.
  • Parse the create-order response body on a non-OK HTTP status (instead of throwing a generic error first), so the existing structured limit, region, and unsupported-currency handling can surface a real message. A non-JSON body still throws a generic error that includes the HTTP status. (In practice Xgram returns HTTP 200 with a structured { result: false, error } body, but parsing on non-OK is the more robust pattern and matches the other central plugins.)
  • Uppercase the Cardano network code (ADA) so it matches the other 27 network identifiers, and update the generated reverse mapping to match. The Xgram ADA pair is currently disabled (CURRENCY_UNSUPPORTED), so the casing is untestable live, but every other code is uppercase.

Testing

  • tsc, eslint, and the mocha suite pass (verify-repo).
  • Verified the reverse-endpoint field semantics directly against the live Xgram API, in both directions, cross-checked against the forward rate. This is the authoritative check for this amount-mapping fix.
  • Built edge-react-gui with this change linked (updot) and baked into the native exchange-plugins webview bundle, launched on the iOS simulator on the edge-funds account, and drove a BTC to ETH swap into the receive-amount entry that triggers the reverse quoteFor='to' path.

Note

Medium Risk
The reverse-quote amount mapping directly affects swap quotes users act on; the rest is fetch/error/mapping hardening with limited blast radius.

Overview
Xgram fixes a functional bug where receive-amount (quoteFor: 'to') quotes showed send and receive swapped, because the reverse API endpoint labels ccyAmountFrom / ccyAmountToExpected opposite to the forward endpoint. createOrder now maps those fields based on isSelling.

Also aligns with other central swap plugins: use fetchCors when the host provides it, and parse the create-order JSON even on non-OK HTTP so existing region, limit, and unsupported-currency handling can surface real errors (non-JSON bodies still fail with a status-only message).

Cardano’s Xgram network code is ADA (uppercase) in both mapping directions.

Reviewed by Cursor Bugbot for commit 4e558de. Bugbot is set up for automated code reviews on this repo. Configure here.

Reverse 'to' quotes use the launch-new-payment-exchange-edge endpoint,
which returns ccyAmountFrom as the receive side and ccyAmountToExpected
as the send side, the opposite of forward quotes. Branch the amount
mapping on isSelling so reverse quotes report the correct send and
receive amounts. Verified against the live Xgram API in both directions.

Also prefer io.fetchCors when available, parse the create-order response
body on non-OK HTTP status so structured limit/region/currency errors
surface instead of a generic failure, and uppercase the Cardano network
code to match the other network identifiers.
@j0ntz j0ntz force-pushed the jon/xgram-405-gap branch from 4bdcd68 to 4a57a53 Compare June 19, 2026 01:00
@j0ntz j0ntz changed the title Address open review findings on the merged Xgram plugin Fix Xgram reverse quote amounts and address open review findings Jun 19, 2026

@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 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 4a57a53. Configure here.

Comment thread src/swap/central/xgram.ts
@j0ntz

j0ntz commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence — in-app Xgram swap executed + reverse-quote fix exercised

agent proof 1210642564105749 01 xgram provider quote

agent proof 1210642564105749 01 xgram provider quote

agent proof 1210642564105749 02 xgram reverse min limit

agent proof 1210642564105749 02 xgram reverse min limit

agent proof 1210642564105749 03 xgram swap success

agent proof 1210642564105749 03 xgram swap success

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

@peachbits peachbits left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is approved but please add a comment above the from and to amount block change describing why on earth we using "to" amount for the "from" field and vice versa.

Explain why the reverse (buy) branch reads ccyAmountToExpected into
fromAmount and ccyAmountFrom into toAmount: the newRevExchange endpoint
labels the amounts opposite to the forward newExchange endpoint. Comment
only, no behavior change.
@j0ntz

j0ntz commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

@peachbits Added an explanatory comment above the from/to amount block in src/swap/central/xgram.ts (commit 4e558de). It documents why the reverse (buy) branch reads ccyAmountToExpected into fromAmount and ccyAmountFrom into toAmount: the newRevExchange endpoint labels the amounts opposite to the forward newExchange endpoint. Comment-only, no behavior change.

@j0ntz j0ntz merged commit 3792899 into master Jun 23, 2026
4 checks passed
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