Skip to content

Partner fixes: Banxa Klarna, getTxInfo disable, and deploy partner updates - #229

Open
paullinator wants to merge 10 commits into
masterfrom
paul/partnerFixes
Open

Partner fixes: Banxa Klarna, getTxInfo disable, and deploy partner updates#229
paullinator wants to merge 10 commits into
masterfrom
paul/partnerFixes

Conversation

@paullinator

@paullinator paullinator commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

  • Add Banxa KLARNA Checkout payment mapping (klarna) to unblock Banxa query progress stuck since Jul 24
  • Disable /v1/getTxInfo (not private enough / scrapable)
  • Bring deploy partner updates onto master: nexchange, xgram, LetsExchange native-network inference, ChangeNow delisted assets, Banxa ZEC historical fallback, and related asset/payment mappings

Test plan

  • Deployed to reports-wusa1; Banxa post-deploy query progressed past Jul 17 Klarna failure through Jul 29 with Successful update and no Klarna errors
  • Redeploy reports-eusa1 when edgy SSH access is available
  • Confirm Banxa continues advancing latestIsoDate on subsequent cycles

Note

Medium Risk
Changes affect partner ingestion progress and USD volume accuracy (token vs native pricing) across multiple plugins; disabling getTxInfo is a behavioral API change for any consumers still calling it.

Overview
Adds n.exchange and xgram swap partners end-to-end (query plugins, queryEngine registration, demo colors) and extends several existing partners so reporting does not stall or mis-price volume.

Banxa maps KLARNA Checkout (and related Primer PayPal/Google Pay) to fiat payment types, adds a ZEC historical catalog fallback, and introduces klarna in shared FiatPaymentType types.

Security / API: /v1/getTxInfo is disabled in indexApi because the endpoint was deemed too easy to scrape.

Accuracy / fail-closed: ChangeNow loads the full currency list (not active=true) so delisted assets still resolve; token createTokenId failures no longer fall back to native pricing. Rango stops swallowing per-transaction errors. LetsExchange infers native networks when the API omits network fields. SideShift adds a delisted SWARMS Solana mapping.

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

Comment thread src/partners/xgram.ts Outdated

@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 3 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Xgram fallbacks overwrite API catalog
    • Reversed the merge so MISSING_CURRENCIES are applied first and the live /list-currency-options catalog overwrites them.
  • ✅ Fixed: LetsExchange drops missing-network throw
    • Restored fail-closed behavior: after NETWORK_FIELDS_AVAILABLE_DATE, an unresolved network throws instead of returning undefined.
  • ✅ Fixed: Nexchange offset after cursor fallback
    • Offset now advances on every page so a cursor-to-offset fallback skips all previously consumed orders.

Create PR

Or push these changes by commenting:

@cursor push 640be9c3ec
Preview (640be9c3ec)
diff --git a/src/partners/letsexchange.ts b/src/partners/letsexchange.ts
--- a/src/partners/letsexchange.ts
+++ b/src/partners/letsexchange.ts
@@ -363,7 +363,10 @@
 ): AssetInfo | undefined {
   const network = resolveNetworkCode(initialNetwork, currencyCode, isoDate)
   if (network == null) {
-    return undefined
+    if (isoDate < NETWORK_FIELDS_AVAILABLE_DATE) {
+      return undefined
+    }
+    throw new Error(`Missing network for currency ${currencyCode}`)
   }
 
   const networkUpper = network.toUpperCase()

diff --git a/src/partners/nexchange.ts b/src/partners/nexchange.ts
--- a/src/partners/nexchange.ts
+++ b/src/partners/nexchange.ts
@@ -351,6 +351,9 @@
 
       if (!hasMore || orders.length === 0) break
 
+      // Always advance offset by the page size so a later cursor→offset
+      // fallback skips every order already consumed via cursors.
+      offset += orders.length
       if (nextCursor != null && nextCursor !== '') {
         cursor = nextCursor
       } else {
@@ -358,7 +361,6 @@
         // cursor value would re-pin pagination to the wrong position next
         // iteration.
         cursor = undefined
-        offset += orders.length
       }
     }
   } catch (e) {

diff --git a/src/partners/xgram.ts b/src/partners/xgram.ts
--- a/src/partners/xgram.ts
+++ b/src/partners/xgram.ts
@@ -272,9 +272,10 @@
   }
 
   const result = await response.json()
+  // Hardcoded fallbacks first so the live catalog can correct/overwrite them.
   currencyCache = {
-    ...asXgramCurrencies(result),
-    ...MISSING_CURRENCIES
+    ...MISSING_CURRENCIES,
+    ...asXgramCurrencies(result)
   }
   currencyCacheTimestamp = Date.now()
   log(`Cached ${Object.keys(currencyCache).length} Xgram currencies`)

You can send follow-ups to the cloud agent here.

Comment thread src/partners/xgram.ts
Comment thread src/partners/letsexchange.ts Outdated
Comment thread src/partners/nexchange.ts
Comment thread src/partners/nexchange.ts
if (!response.ok) {
const text = await response.text()
throw new Error(
`HTTP ${response.status.toString()}: ${truncateForError(text)}`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Agentic Security Review
Severity: MEDIUM
This path still includes partner-provided HTTP error body content in thrown errors (${truncateForError(text)}), and the outer catch logs String(e). Even with truncation, upstream diagnostics can include sensitive transaction/customer fields and end up in application logs.

Impact: Sensitive partner/customer response data may be retained in logs, increasing exposure surface for internal log readers and downstream log pipelines.

Fix in Cursor Fix in Web

Reviewed by Cursor Security Reviewer for commit cad25fb. Configure here.

@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 2 potential issues.

Autofix Details

Done

Create PR

Or push these changes by commenting:

@cursor push d9d80f6f18
Preview (d9d80f6f18)
diff --git a/src/partners/letsexchange.ts b/src/partners/letsexchange.ts
--- a/src/partners/letsexchange.ts
+++ b/src/partners/letsexchange.ts
@@ -202,13 +202,13 @@
 }
 
 // When the API omits network fields, infer native chain from currency code.
-// Only unambiguous 1:1 native-ticker cases (not USDT, USDC, BNB, etc.).
+// Only unambiguous 1:1 native-ticker cases (not USDT, USDC, BNB, ETH, AVAX,
+// MATIC, FTM, etc. — those are multi-chain or also exist as ERC-20 tokens).
 const LETSEXCHANGE_CURRENCY_TO_DEFAULT_NETWORK: Record<string, string> = {
   ADA: 'ADA',
   ALGO: 'ALGO',
   ARRR: 'ARRR',
   ATOM: 'ATOM',
-  AVAX: 'AVAXC',
   BCH: 'BCH',
   BSV: 'BSV',
   BTC: 'BTC',
@@ -221,16 +221,13 @@
   DOT: 'DOT',
   EOS: 'EOS',
   ETC: 'ETC',
-  ETH: 'ETH',
   ETHW: 'ETHW',
   FIL: 'FIL',
   FIO: 'FIO',
   FIRO: 'FIRO',
-  FTM: 'FTM',
   GRS: 'GRS',
   HBAR: 'HBAR',
   LTC: 'LTC',
-  MATIC: 'MATIC',
   PIVX: 'PIVX',
   POL: 'POL',
   PLS: 'PLS',

diff --git a/src/partners/xgram.ts b/src/partners/xgram.ts
--- a/src/partners/xgram.ts
+++ b/src/partners/xgram.ts
@@ -449,7 +449,16 @@
     }
     let oldestIsoDate = '999999999999999999999999999999999999'
     for (const rawTx of txs) {
-      const standardTx = processXgramTx(rawTx, currencies)
+      let standardTx: StandardTx
+      try {
+        standardTx = processXgramTx(rawTx, currencies)
+      } catch (e) {
+        log.error(String(e))
+        // Process failure: stop without advancing the watermark, but keep
+        // already-collected txs so they can still be persisted.
+        done = true
+        break
+      }
       if (standardTx.isoDate < oldestIsoDate) {
         oldestIsoDate = standardTx.isoDate
       }

You can send follow-ups to the cloud agent here.

Comment thread src/partners/xgram.ts
standardTxs.push(standardTx)
if (standardTx.isoDate > newLatestIsoDate) {
newLatestIsoDate = standardTx.isoDate
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Xgram drops processed txs on error

High Severity

processXgramTx errors are uncaught, so they abort queryXgram before it returns. Fetch failures in the same function break and still return already-collected txs without advancing the watermark, but a process/clean failure discards those txs entirely. Because Xgram pages newest-to-oldest, one unmapped older order blocks persistence of all newer volume until the mapping is fixed.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cad25fb. Configure here.

XTZ: 'XTZ',
ZANO: 'ZANO',
ZEC: 'ZEC'
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ambiguous native network inference

Medium Severity

LETSEXCHANGE_CURRENCY_TO_DEFAULT_NETWORK claims only unambiguous 1:1 native tickers, but includes multi-chain symbols such as ETH, MATIC, FTM, and AVAX. When LetsExchange omits network on a post-cutoff tx, those codes are forced onto a single chain, so L2/native or ERC-20 variants can get the wrong chainPluginId and be priced incorrectly.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cad25fb. Configure here.

@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.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

Done

Create PR

Or push these changes by commenting:

@cursor push b538a449e1
Preview (b538a449e1)
diff --git a/src/util/chainIds.ts b/src/util/chainIds.ts
--- a/src/util/chainIds.ts
+++ b/src/util/chainIds.ts
@@ -13,6 +13,7 @@
   fantom: 250,
   filecoinfevm: 314,
   hyperevm: 999,
+  monad: 143,
   optimism: 10,
   opbnb: 204,
   polygon: 137,

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 018948c. Configure here.

Comment thread src/partners/rango.ts
FANTOM: 'fantom',
LTC: 'litecoin',
MATIC: 'polygon',
MONAD: 'monad',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Monad chain ID never set

Medium Severity

Mapping Rango MONAD to monad unblocks those txs, but EVM_CHAIN_IDS has no monad entry, so depositEvmChainId / payoutEvmChainId stay undefined. Before this change an unknown Monad chain threw; now the rows persist with incomplete EVM enrichment even though tokenTypes already treats monad as an EVM chain.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 018948c. Configure here.

@cursor

cursor Bot commented Jul 30, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

MMrj9 and others added 10 commits July 30, 2026 09:22
Co-authored-by: Cursor <cursoragent@cursor.com>
When an asset has a contract address it is a token, but several plugins
silently fell back to a native (tokenId: null) mapping when the token
could not be resolved. That prices the token with the chain's gas-token
rate and overcounts volume whenever the token is worth less than the gas
token.

- nexchange: throw when a contract-bearing asset is on a chain whose
  tokenType is missing, instead of returning tokenId: null.
- changenow: drop the try/catch around createTokenId that swallowed
  failures and returned tokenId: null.
- rango: drop the per-tx try/catch that logged and continued, silently
  dropping any transaction whose asset could not be resolved.

All three plugins paginate oldest-to-newest and persist progress on
throw, so a failing order halts and is retried next run rather than being
mispriced or silently dropped.

Also add the SUI and MONAD chain mappings to rango: these were
previously dropped silently and would now halt the plugin. Verified by
reprocessing the last three months of orders (nexchange 22.7k, changenow
61.9k, rango 2.9k) with zero processing failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Some older LetsExchange transactions return null network fields for
unambiguous native assets (e.g. ETH, BTC, XRP), causing processing to
throw "Missing network" and stalling the query. Add a currency-to-network
fallback for 1:1 native tickers so these transactions process correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>
These mappings were causing the query engine to halt and stop scanning
forward for the affected partners (fail-closed design), blocking
collection of newer transactions until the mapping was added.

- SideShift: map SWARMS on Solana to its mint address (delisted from the
  SideShift coins API, so added to DELISTED_COINS).
- Rango: map the SONIC blockchain to the `sonic` Edge pluginId.
- Banxa: map the "Primer Paypal Pay" and "Primer Google Pay" payment
  types to paypal and googlepay respectively.

Co-authored-by: Cursor <cursoragent@cursor.com>
The currency cache was built from the `currencies?active=true` endpoint.
When ChangeNow deactivates an asset (e.g. DASH), it disappears from the
active list while historical transactions still reference it. The lookup
then misses and the plugin halts fail-closed, stalling all ChangeNow
transaction collection.

Fetch the full currency list (omit `active=true`) so previously-listed
assets continue to resolve for historical transactions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Banxa removed ZEC from the v2 crypto catalog, so historical ZEC
orders abort the partner query and stall ingestion past July 8.

Co-authored-by: Cursor <cursoragent@cursor.com>
Not private enough and is scrapable.

Co-authored-by: Cursor <cursoragent@cursor.com>
Unblocks Banxa query progress stuck since Jul 24 on unrecognized
KLARNA Checkout payment methods.

Co-authored-by: Cursor <cursoragent@cursor.com>
Rango maps MONAD to the monad pluginId, but without a numeric chain id
depositEvmChainId/payoutEvmChainId stay undefined for Monad EVM txs.
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.

3 participants