Skip to content

Commit 4d53fdc

Browse files
committed
Fix partner processTx signatures
1 parent 5025d6d commit 4d53fdc

4 files changed

Lines changed: 95 additions & 25 deletions

File tree

src/partners/nexchange.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ export type NexchangeCurrencyInfoMap = Record<string, NexchangeCurrencyMeta>
6868
const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 5 // 5 days
6969
const LIMIT = 200
7070
const MAX_ERROR_TEXT_LENGTH = 500
71+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours
72+
73+
let currencyCache: NexchangeCurrencyInfoMap | undefined
74+
let currencyCacheTimestamp = 0
7175

7276
const statusMap: { [key: string]: Status } = {
7377
released: 'complete',
@@ -176,6 +180,13 @@ function truncateForError(text: string): string {
176180
export async function fetchNexchangeCurrencyMap(): Promise<
177181
NexchangeCurrencyInfoMap
178182
> {
183+
if (
184+
currencyCache != null &&
185+
Date.now() - currencyCacheTimestamp < CACHE_TTL_MS
186+
) {
187+
return currencyCache
188+
}
189+
179190
const response = await retryFetch(CURRENCY_URL, { method: 'GET' })
180191
if (!response.ok) {
181192
const text = await response.text()
@@ -189,9 +200,21 @@ export async function fetchNexchangeCurrencyMap(): Promise<
189200
for (const currency of currencies) {
190201
map[currency.code.toUpperCase()] = currency
191202
}
203+
currencyCache = map
204+
currencyCacheTimestamp = Date.now()
192205
return map
193206
}
194207

208+
async function loadNexchangeCurrencyMap(
209+
pluginParams: PluginParams
210+
): Promise<NexchangeCurrencyInfoMap> {
211+
const { currencyMap } = (pluginParams as unknown) as {
212+
currencyMap?: NexchangeCurrencyInfoMap
213+
}
214+
if (currencyMap != null) return currencyMap
215+
return await fetchNexchangeCurrencyMap()
216+
}
217+
195218
/**
196219
* Returned by `resolveNexchangeAsset`. The shape is consistent across all
197220
* exit branches so callers can rely on the field set. `chainPluginId`,
@@ -315,7 +338,7 @@ export async function queryNexchange(
315338
// audit-orders endpoint omits, so it is required for chain/token
316339
// enrichment. Fetch it up front; a failure aborts the run (saving
317340
// nothing) rather than persisting a batch of unenriched transactions.
318-
const currencyMap = await fetchNexchangeCurrencyMap()
341+
await fetchNexchangeCurrencyMap()
319342

320343
while (true) {
321344
const params: string[] = [
@@ -341,7 +364,7 @@ export async function queryNexchange(
341364
const { orders, nextCursor, hasMore } = asNexchangeOrdersResponse(json)
342365

343366
for (const rawOrder of orders) {
344-
const standardTx = processNexchangeTx(rawOrder, currencyMap)
367+
const standardTx = await processNexchangeTx(rawOrder, pluginParams)
345368
txByOrderId.set(standardTx.orderId, standardTx)
346369
if (standardTx.isoDate > latestIsoDate) {
347370
latestIsoDate = standardTx.isoDate
@@ -382,11 +405,12 @@ export const nexchange: PartnerPlugin = {
382405
pluginId: 'nexchange'
383406
}
384407

385-
export function processNexchangeTx(
408+
export async function processNexchangeTx(
386409
rawTx: unknown,
387-
currencyMap: NexchangeCurrencyInfoMap
388-
): StandardTx {
410+
pluginParams: PluginParams
411+
): Promise<StandardTx> {
389412
const tx = asNexchangeOrder(rawTx)
413+
const currencyMap = await loadNexchangeCurrencyMap(pluginParams)
390414
const lowerStatus = tx.status.toLowerCase()
391415
const status = statusMap[lowerStatus] ?? 'other'
392416
const { isoDate, timestamp } = parseApiDate(tx.createdAt)

src/partners/xgram.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,23 @@ async function fetchCurrencyCache(
281281
return currencyCache
282282
}
283283

284+
async function loadXgramCurrencies(
285+
pluginParams: PluginParams
286+
): Promise<XgramCurrencies> {
287+
const { currencies } = (pluginParams as unknown) as {
288+
currencies?: XgramCurrencies
289+
}
290+
if (currencies != null) return currencies
291+
292+
const { log } = pluginParams
293+
const { apiKeys } = asStandardPluginParams(pluginParams)
294+
const { apiKey } = apiKeys
295+
if (apiKey == null) {
296+
throw new Error('Xgram apiKey required for asset info lookup')
297+
}
298+
return await fetchCurrencyCache(apiKey, log)
299+
}
300+
284301
function isNativeTicker(chainPluginId: string, currencyCode: string): boolean {
285302
return NATIVE_TICKERS[chainPluginId]?.has(currencyCode.toUpperCase()) ?? false
286303
}
@@ -375,7 +392,7 @@ export const queryXgram = async (
375392
if (previousTimestamp < 0) previousTimestamp = 0
376393
const targetIsoDate = new Date(previousTimestamp).toISOString()
377394

378-
const currencies = await fetchCurrencyCache(apiKey, log)
395+
await fetchCurrencyCache(apiKey, log)
379396

380397
// Because Xgram pages from newest to oldest, the watermark can only be
381398
// advanced once the entire newer-than-target range has been fetched and
@@ -427,7 +444,7 @@ export const queryXgram = async (
427444
}
428445
let oldestIsoDate = '999999999999999999999999999999999999'
429446
for (const rawTx of txs) {
430-
const standardTx = processXgramTx(rawTx, currencies)
447+
const standardTx = await processXgramTx(rawTx, pluginParams)
431448
if (standardTx.isoDate < oldestIsoDate) {
432449
oldestIsoDate = standardTx.isoDate
433450
}
@@ -462,11 +479,12 @@ export const xgram: PartnerPlugin = {
462479
pluginId: 'xgram'
463480
}
464481

465-
export function processXgramTx(
482+
export async function processXgramTx(
466483
rawTx: unknown,
467-
currencies: XgramCurrencies
468-
): StandardTx {
484+
pluginParams: PluginParams
485+
): Promise<StandardTx> {
469486
const tx: XgramTxTx = asXgramTx(rawTx)
487+
const currencies = await loadXgramCurrencies(pluginParams)
470488
const { isoDate, timestamp } = parseXgramDate(tx.date)
471489
const depositCurrency = tx['x-fromCcy'].toUpperCase()
472490
const payoutCurrency = tx['x-toCcy'].toUpperCase()

test/nexchange.test.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
resolveNexchangeAsset,
1010
toQueryIsoDate
1111
} from '../src/partners/nexchange'
12+
import { PluginParams } from '../src/types'
1213

1314
const currencyMap: NexchangeCurrencyInfoMap = {
1415
BTC: {
@@ -92,6 +93,19 @@ const currencyMap: NexchangeCurrencyInfoMap = {
9293
}
9394
}
9495

96+
const testLog = Object.assign(() => {}, {
97+
warn: () => {},
98+
error: () => {}
99+
})
100+
const pluginParams: PluginParams & {
101+
currencyMap: NexchangeCurrencyInfoMap
102+
} = {
103+
settings: {},
104+
apiKeys: {},
105+
log: testLog,
106+
currencyMap
107+
}
108+
95109
function makeRawOrder(overrides: { [key: string]: any } = {}): unknown {
96110
return {
97111
orderId: 'NEX-DEFAULT',
@@ -116,10 +130,10 @@ function makeRawOrder(overrides: { [key: string]: any } = {}): unknown {
116130

117131
describe('nexchange plugin', () => {
118132
describe('processNexchangeTx', () => {
119-
it('maps Edge audit order payload into StandardTx with chain plugin and token ids', () => {
120-
const tx = processNexchangeTx(
133+
it('maps Edge audit order payload into StandardTx with chain plugin and token ids', async () => {
134+
const tx = await processNexchangeTx(
121135
makeRawOrder({ orderId: 'NEX-ABCD1234' }),
122-
currencyMap
136+
pluginParams
123137
)
124138

125139
expect(tx.orderId).to.equal('NEX-ABCD1234')
@@ -159,10 +173,10 @@ describe('nexchange plugin', () => {
159173
['something-else', 'other']
160174
]
161175
for (const [rawStatus, expected] of statusCases) {
162-
it(`maps status "${rawStatus}" to "${expected}"`, () => {
163-
const tx = processNexchangeTx(
176+
it(`maps status "${rawStatus}" to "${expected}"`, async () => {
177+
const tx = await processNexchangeTx(
164178
makeRawOrder({ status: rawStatus }),
165-
currencyMap
179+
pluginParams
166180
)
167181
expect(tx.status).to.equal(expected)
168182
})

test/xgram.test.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { expect } from 'chai'
22
import { describe, it } from 'mocha'
33

44
import { processXgramTx, XgramCurrencies } from '../src/partners/xgram'
5+
import { PluginParams } from '../src/types'
56

67
const currencies: XgramCurrencies = {
78
BTC: {
@@ -31,9 +32,22 @@ const currencies: XgramCurrencies = {
3132
}
3233
}
3334

35+
const testLog = Object.assign(() => {}, {
36+
warn: () => {},
37+
error: () => {}
38+
})
39+
const pluginParams: PluginParams & {
40+
currencies: XgramCurrencies
41+
} = {
42+
settings: {},
43+
apiKeys: {},
44+
log: testLog,
45+
currencies
46+
}
47+
3448
describe('processXgramTx', () => {
35-
it('maps source and destination asset IDs', () => {
36-
const tx = processXgramTx(
49+
it('maps source and destination asset IDs', async () => {
50+
const tx = await processXgramTx(
3751
{
3852
id: 'dyv3a2tdbgipvh0',
3953
'x-status': 'x-completed',
@@ -49,7 +63,7 @@ describe('processXgramTx', () => {
4963
date: '27.05.2026 20:57:28',
5064
txId: 'payout-hash'
5165
},
52-
currencies
66+
pluginParams
5367
)
5468

5569
expect(tx.status).equals('complete')
@@ -66,8 +80,8 @@ describe('processXgramTx', () => {
6680
expect(tx.isoDate).equals('2026-05-27T20:57:28.000Z')
6781
})
6882

69-
it('uses expected amounts and chain-specific token IDs for pending rows', () => {
70-
const tx = processXgramTx(
83+
it('uses expected amounts and chain-specific token IDs for pending rows', async () => {
84+
const tx = await processXgramTx(
7185
{
7286
id: 'tmah3a2td9cp20q0',
7387
'x-status': 'x-new',
@@ -83,7 +97,7 @@ describe('processXgramTx', () => {
8397
date: '27.05.2026 20:56:54',
8498
txId: null
8599
},
86-
currencies
100+
pluginParams
87101
)
88102

89103
expect(tx.status).equals('pending')
@@ -97,8 +111,8 @@ describe('processXgramTx', () => {
97111
expect(tx.payoutTokenId).equals(null)
98112
})
99113

100-
it('maps historical native currencies missing from the currency API', () => {
101-
const tx = processXgramTx(
114+
it('maps historical native currencies missing from the currency API', async () => {
115+
const tx = await processXgramTx(
102116
{
103117
id: 'talr3a0e49fplpog',
104118
'x-status': 'x-timeout',
@@ -114,7 +128,7 @@ describe('processXgramTx', () => {
114128
date: '12.05.2026 20:07:51',
115129
txId: null
116130
},
117-
currencies
131+
pluginParams
118132
)
119133

120134
expect(tx.status).equals('expired')

0 commit comments

Comments
 (0)