Partner fixes: Banxa Klarna, getTxInfo disable, and deploy partner updates - #229
Partner fixes: Banxa Klarna, getTxInfo disable, and deploy partner updates#229paullinator wants to merge 10 commits into
Conversation
There was a problem hiding this comment.
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.
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.
| if (!response.ok) { | ||
| const text = await response.text() | ||
| throw new Error( | ||
| `HTTP ${response.status.toString()}: ${truncateForError(text)}` |
There was a problem hiding this comment.
🔒 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.
Reviewed by Cursor Security Reviewer for commit cad25fb. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Autofix Details
Done
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.
| standardTxs.push(standardTx) | ||
| if (standardTx.isoDate > newLatestIsoDate) { | ||
| newLatestIsoDate = standardTx.isoDate | ||
| } |
There was a problem hiding this comment.
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)
Reviewed by Cursor Bugbot for commit cad25fb. Configure here.
| XTZ: 'XTZ', | ||
| ZANO: 'ZANO', | ||
| ZEC: 'ZEC' | ||
| } |
There was a problem hiding this comment.
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)
Reviewed by Cursor Bugbot for commit cad25fb. Configure here.
cad25fb to
018948c
Compare
There was a problem hiding this comment.
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).
Done
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.
| FANTOM: 'fantom', | ||
| LTC: 'litecoin', | ||
| MATIC: 'polygon', | ||
| MONAD: 'monad', |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit 018948c. Configure here.
Bugbot is paused — on-demand spend limit reachedBugbot 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. |
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>
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.
b538a44 to
a6407a1
Compare



Summary
KLARNA Checkoutpayment mapping (klarna) to unblock Banxa query progress stuck since Jul 24/v1/getTxInfo(not private enough / scrapable)Test plan
latestIsoDateon subsequent cyclesNote
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,
queryEngineregistration, 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 introducesklarnain sharedFiatPaymentTypetypes.Security / API:
/v1/getTxInfois disabled inindexApibecause 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; tokencreateTokenIdfailures 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.