Skip to content

Commit b308478

Browse files
committed
Harden Sideshift order processing against unmapped coins
Map bsv (Bitcoin SV) to the bitcoinsv plugin id and add BSV-bsv to the delisted coins map so historical BSV orders resolve instead of throwing "Unknown network: bsv". Also skip and log a single unprocessable order rather than letting it abort the page. A throw left the per-account cursor stuck before the bad order, so the account retried then gave up every cycle forever. The dual-account change makes from-epoch backfills routine, which makes hitting such an order far more likely.
1 parent ecde277 commit b308478

2 files changed

Lines changed: 56 additions & 1 deletion

File tree

src/partners/sideshift.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const SIDESHIFT_NETWORK_TO_PLUGIN_ID: ChainNameToPluginIdMapping = {
3636
bitcoin: 'bitcoin',
3737
bitcoincash: 'bitcoincash',
3838
bsc: 'binancesmartchain',
39+
bsv: 'bitcoinsv',
3940
cardano: 'cardano',
4041
cosmos: 'cosmoshub',
4142
dash: 'dash',
@@ -72,6 +73,7 @@ const ASSET_NAME_OVERRIDES: Record<string, string> = {
7273
// Delisted coins that are no longer in the SideShift API
7374
// Map: `${coin}-${network}` -> contract address (null for native gas tokens)
7475
const DELISTED_COINS: Record<string, string | null> = {
76+
'BSV-bsv': null, // Native gas token
7577
'BUSD-bsc': '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
7678
'FTM-fantom': null, // Native gas token
7779
'MATIC-ethereum': '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0',
@@ -337,7 +339,20 @@ async function querySideshiftAccount(
337339
break
338340
}
339341
for (const rawTx of orders) {
340-
const standardTx = await processOrder(rawTx)
342+
let standardTx: StandardTx
343+
try {
344+
standardTx = await processOrder(rawTx)
345+
} catch (e) {
346+
// A single unprocessable order (e.g. a coin or network missing from
347+
// the plugin maps) must not stall the account. Without skipping, the
348+
// throw aborts the page, the cursor never advances past it, and the
349+
// account retries-then-gives-up every cycle forever. Log the gap so
350+
// it can be mapped, then move on.
351+
log.warn(
352+
`${account.affiliateId} skipping unprocessable order: ${String(e)}`
353+
)
354+
continue
355+
}
341356
standardTxs.push(standardTx)
342357
if (standardTx.isoDate > latestIsoDate) {
343358
latestIsoDate = standardTx.isoDate

test/sideshift.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,44 @@ describe('querySideshift dual-account merge', function() {
259259
new Date('2025-03-01T00:00:00.000Z').getTime() - QUERY_LOOKBACK
260260
)
261261
})
262+
263+
it('skips an unprocessable order instead of stalling the account', async function() {
264+
// A coin or network missing from the plugin maps makes processing throw.
265+
// The account must drop that order, keep the rest, and advance its cursor
266+
// past the bad order rather than retrying-then-giving-up forever.
267+
const { fetchOrders } = makeFakeFetcher({
268+
PRIMARY: [
269+
{ orderId: 'good1', isoDate: '2025-06-01T00:00:00.000Z' },
270+
{ orderId: 'bad', isoDate: '2025-06-02T00:00:00.000Z' },
271+
{ orderId: 'good2', isoDate: '2025-06-03T00:00:00.000Z' }
272+
]
273+
})
274+
const throwingProcess = async (rawTx: unknown): Promise<StandardTx> => {
275+
const order = rawTx as RawOrder
276+
if (order.orderId === 'bad') throw new Error('Unknown network: bsv')
277+
return makeTx(order.orderId, order.isoDate)
278+
}
279+
const result = await querySideshift(
280+
{
281+
apiKeys: {
282+
sideshiftAffiliateId: 'PRIMARY',
283+
sideshiftAffiliateSecret: 'sprimary'
284+
},
285+
settings: {},
286+
log: noopLog
287+
},
288+
fetchOrders,
289+
throwingProcess
290+
)
291+
292+
// The bad order is dropped; the good ones survive.
293+
expect(result.transactions.map(tx => tx.orderId)).to.deep.equal([
294+
'good1',
295+
'good2'
296+
])
297+
// Cursor advanced to the newest good order, not stuck before 'bad'.
298+
expect(result.settings.accounts.PRIMARY).to.equal(
299+
'2025-06-03T00:00:00.000Z'
300+
)
301+
})
262302
})

0 commit comments

Comments
 (0)