From b2cdf17326714675f81bcb7757bdf9faf61f82b5 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Mon, 6 Jan 2025 14:50:28 -0800 Subject: [PATCH 01/35] Add comments to config definition --- src/config.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config.ts b/src/config.ts index 9bfc4083..92b904f4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -8,8 +8,12 @@ export const asConfig = asObject({ ), httpPort: asOptional(asNumber, 8008), bog: asOptional(asObject({ apiKey: asString }), { apiKey: '' }), + + /** Only run specific appIds (e.g. edge, coinhub, etc) */ soloAppIds: asOptional(asArray(asString), null), + /** Only run specific partnerIds (e.g. moonpay, paybis, etc) */ soloPartnerIds: asOptional(asArray(asString), null), + timeoutOverrideMins: asOptional(asNumber, 1200), cacheLookbackMonths: asOptional(asNumber, 24) }) From aa136dc5818752e4abfcc839f58ff0c20b72396d Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Mon, 6 Jan 2025 16:30:48 -0800 Subject: [PATCH 02/35] Upgrade cleaners@^0.3.17 This upgrade gives us more information about the raw value's type when a cleaner fails. Very useful. --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f0d89874..9947064b 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "biggystring": "^4.1.3", "body-parser": "^1.19.0", "cleaner-config": "^0.1.10", - "cleaners": "^0.3.13", + "cleaners": "^0.3.17", "commander": "^6.1.0", "cors": "^2.8.5", "csv-stringify": "^6.2.0", diff --git a/yarn.lock b/yarn.lock index 3b13a007..4dbe53d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2073,10 +2073,10 @@ cleaner-config@^0.1.10: minimist "^1.2.5" sucrase "^3.17.1" -cleaners@^0.3.13: - version "0.3.13" - resolved "https://registry.npmjs.org/cleaners/-/cleaners-0.3.13.tgz" - integrity sha512-sCedc8LIXUhLmXT9rkkAToi9mjYhI7J/gKRWiF0Qw6eC0ymILHxq+vhuaKoKdcSWpYi2YqqwSlvNtD+92gf4pA== +cleaners@^0.3.17: + version "0.3.17" + resolved "https://registry.yarnpkg.com/cleaners/-/cleaners-0.3.17.tgz#dae498f3d49b7e9364050402d2f4ad09abcd31ba" + integrity sha512-X5acjsLwJK+JEK5hv0Rve7G78+E6iYh1TzJZ40z7Yjrba0WhW6spTq28WgG9w+AK+YQIOHtQTrzaiuntMBBIwQ== cleaners@^0.3.8: version "0.3.16" From 3c0dab2c2a73b60132894a11c179a10ccca51a5d Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 14:47:50 -0800 Subject: [PATCH 03/35] Factor out `processBanxaTx` function --- src/partners/banxa.ts | 95 ++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/src/partners/banxa.ts b/src/partners/banxa.ts index c65f83b3..5780ab34 100644 --- a/src/partners/banxa.ts +++ b/src/partners/banxa.ts @@ -202,61 +202,33 @@ async function fetchBanxaAPI( return await retryFetch(`${partnerUrl}${apiQuery}`, { headers: headers }) } -function processBanxaOrders(rawtxs, ssFormatTxs): void { +function processBanxaOrders( + rawtxs: unknown[], + ssFormatTxs: StandardTx[] +): void { let numComplete = 0 let newestIsoDate = new Date(0).toISOString() let oldestIsoDate = new Date(9999999999999).toISOString() for (const rawTx of rawtxs) { - let tx: BanxaTx + let standardTx: StandardTx try { - tx = asBanxaTx(rawTx) + standardTx = processBanxaTx(rawTx) } catch (e) { datelog(String(e)) throw e } - if (tx.status === 'complete') { - numComplete++ - } - const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.created_at) - if (isoDate > newestIsoDate) { - newestIsoDate = isoDate - } - if (isoDate < oldestIsoDate) { - oldestIsoDate = isoDate + ssFormatTxs.push(standardTx) + + if (standardTx.status === 'complete') { + numComplete++ } - // Flip the amounts if the order is a SELL - let payoutAddress - let inputAmount = tx.fiat_amount - let inputCurrency = tx.fiat_code - let outputAmount = tx.coin_amount - let outputCurrency = tx.coin_code - if (tx.order_type === 'CRYPTO-SELL') { - inputAmount = tx.coin_amount - inputCurrency = tx.coin_code - outputAmount = tx.fiat_amount - outputCurrency = tx.fiat_code - } else { - payoutAddress = tx.wallet_address + if (standardTx.isoDate > newestIsoDate) { + newestIsoDate = standardTx.isoDate } - - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: inputCurrency, - depositAmount: inputAmount, - payoutTxid: undefined, - payoutAddress, - payoutCurrency: outputCurrency, - payoutAmount: outputAmount, - timestamp, - isoDate, - usdValue: -1, - rawTx + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - ssFormatTxs.push(ssTx) } if (rawtxs.length > 1) { datelog( @@ -271,3 +243,42 @@ function processBanxaOrders(rawtxs, ssFormatTxs): void { datelog(`BANXA: Processed ${rawtxs.length}`) } } + +export function processBanxaTx(rawTx: unknown): StandardTx { + const banxaTx: BanxaTx = asBanxaTx(rawTx) + const { isoDate, timestamp } = smartIsoDateFromTimestamp(banxaTx.created_at) + + // Flip the amounts if the order is a SELL + let payoutAddress + let inputAmount = banxaTx.fiat_amount + let inputCurrency = banxaTx.fiat_code + let outputAmount = banxaTx.coin_amount + let outputCurrency = banxaTx.coin_code + if (banxaTx.order_type === 'CRYPTO-SELL') { + inputAmount = banxaTx.coin_amount + inputCurrency = banxaTx.coin_code + outputAmount = banxaTx.fiat_amount + outputCurrency = banxaTx.fiat_code + } else { + payoutAddress = banxaTx.wallet_address + } + + const standardTx: StandardTx = { + status: statusMap[banxaTx.status], + orderId: banxaTx.id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: inputCurrency, + depositAmount: inputAmount, + payoutTxid: undefined, + payoutAddress, + payoutCurrency: outputCurrency, + payoutAmount: outputAmount, + timestamp, + isoDate, + usdValue: -1, + rawTx + } + + return standardTx +} From 6ce79fee4d31bc38ce1309d6097542344533db6e Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:08:52 -0800 Subject: [PATCH 04/35] Factor out `processBitaccessTx` function --- src/partners/bitaccess.ts | 70 +++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/src/partners/bitaccess.ts b/src/partners/bitaccess.ts index efd53745..56a27bdb 100644 --- a/src/partners/bitaccess.ts +++ b/src/partners/bitaccess.ts @@ -53,7 +53,7 @@ export async function queryBitaccess( } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] lastTimestamp -= QUERY_LOOKBACK let newestTimestamp = 0 let page = 1 @@ -83,38 +83,13 @@ export async function queryBitaccess( const txs = result.result for (const rawTx of txs) { if (asBitaccessRawTx(rawTx).status === 'complete') { - const tx = asBitaccessTx(rawTx) - const timestamp = new Date(tx.updated_at).getTime() / 1000 - let depositTxid - let payoutTxid - if (typeof tx.deposit_address === 'string') { - depositTxid = tx.tx_hash - } - if (typeof tx.withdrawal_address === 'string') { - payoutTxid = tx.tx_hash - } + const standardTx = processBitaccessTx(rawTx) + standardTxs.push(standardTx) - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.transaction_id, - depositTxid, - depositAddress: tx.deposit_address, - depositCurrency: tx.deposit_currency.toUpperCase(), - depositAmount: tx.deposit_amount, - payoutTxid, - payoutAddress: tx.withdrawal_address, - payoutCurrency: tx.withdrawal_currency.toUpperCase(), - payoutAmount: tx.withdrawal_amount, - timestamp, - isoDate: tx.updated_at, - usdValue: -1, - rawTx - } - ssFormatTxs.push(ssTx) - if (timestamp > newestTimestamp) { - newestTimestamp = timestamp + if (standardTx.timestamp > newestTimestamp) { + newestTimestamp = standardTx.timestamp } - if (timestamp < lastTimestamp) { + if (standardTx.timestamp < lastTimestamp) { done = true break } @@ -132,7 +107,7 @@ export async function queryBitaccess( const out = { settings: { lastTimestamp: newestTimestamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -144,3 +119,34 @@ export const bitaccess: PartnerPlugin = { pluginName: 'Bitaccess', pluginId: 'bitaccess' } + +export function processBitaccessTx(rawTx: unknown): StandardTx { + const tx = asBitaccessTx(rawTx) + const timestamp = new Date(tx.updated_at).getTime() / 1000 + let depositTxid + let payoutTxid + if (typeof tx.deposit_address === 'string') { + depositTxid = tx.tx_hash + } + if (typeof tx.withdrawal_address === 'string') { + payoutTxid = tx.tx_hash + } + + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.transaction_id, + depositTxid, + depositAddress: tx.deposit_address, + depositCurrency: tx.deposit_currency.toUpperCase(), + depositAmount: tx.deposit_amount, + payoutTxid, + payoutAddress: tx.withdrawal_address, + payoutCurrency: tx.withdrawal_currency.toUpperCase(), + payoutAmount: tx.withdrawal_amount, + timestamp, + isoDate: tx.updated_at, + usdValue: -1, + rawTx + } + return standardTx +} From 09ee5f901ed67d0ced91cb70243e6ccd7f4cf787 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:19:58 -0800 Subject: [PATCH 05/35] Factor out `processBitrefillTx` function --- .vscode/settings.json | 3 +- src/partners/bitrefill.ts | 93 ++++++++++++++++++++++----------------- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a73a41b..f4199a36 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,2 +1,3 @@ { -} \ No newline at end of file + "cSpell.words": ["Bitrefill"] +} diff --git a/src/partners/bitrefill.ts b/src/partners/bitrefill.ts index c3df90a1..ce759c3a 100644 --- a/src/partners/bitrefill.ts +++ b/src/partners/bitrefill.ts @@ -27,7 +27,11 @@ const asBitrefillTx = asObject({ usdPrice: asNumber }) -const asRawBitrefillTx = asObject({ +// Partial type for Bitrefill txs for pre-processing +const asPreBitrefillTx = asObject({ + expired: asBoolean, + paymentReceived: asBoolean, + sent: asBoolean, status: asString }) @@ -66,7 +70,7 @@ export async function queryBitrefill( Authorization: 'Basic ' + Buffer.from(username + ':' + password).toString('base64') } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let url = `https://api.bitrefill.com/v1/orders/` let count = 0 @@ -83,46 +87,15 @@ export async function queryBitrefill( break } const txs = jsonObj.orders - for (const rawtx of txs) { - if (asRawBitrefillTx(rawtx).status === 'unpaid') { + for (const rawTx of txs) { + // Pre-process the tx to see if it's meets criteria for inclusion: + const preTx = asPreBitrefillTx(rawTx) + if (preTx.status === 'unpaid') { continue } - const tx = asBitrefillTx(rawtx) - if (tx.paymentReceived && !tx.expired && tx.sent) { - const timestamp = tx.invoiceTime / 1000 - - let inputAmountStr = tx.satoshiPrice?.toString() - const inputCurrency: string = tx.coinCurrency.toUpperCase() - if (typeof multipliers[inputCurrency] !== 'string') { - datelog(inputCurrency + ' has no multipliers') - break - } - if (typeof inputCurrency === 'string' && inputCurrency !== 'BTC') { - inputAmountStr = tx.receivedPaymentAltcoin?.toString() - } - if (inputAmountStr == null) { - break - } - const inputAmountNum = safeParseFloat( - div(inputAmountStr, multipliers[inputCurrency], 8) - ) - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.orderId, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: inputCurrency, - depositAmount: inputAmountNum, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: tx.currency, - payoutAmount: parseInt(tx.value), - timestamp, - isoDate: new Date(tx.invoiceTime).toISOString(), - usdValue: tx.usdPrice, - rawTx: rawtx - } - ssFormatTxs.push(ssTx) + if (preTx.paymentReceived && !preTx.expired && preTx.sent) { + const standardTx = processBitrefillTx(rawTx) + standardTxs.push(standardTx) } } @@ -139,7 +112,7 @@ export async function queryBitrefill( } const out: PluginResult = { settings: {}, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -151,3 +124,41 @@ export const bitrefill: PartnerPlugin = { pluginName: 'Bitrefill', pluginId: 'bitrefill' } + +export function processBitrefillTx(rawTx: unknown): StandardTx { + const tx = asBitrefillTx(rawTx) + const timestamp = tx.invoiceTime / 1000 + + const inputCurrency: string = tx.coinCurrency.toUpperCase() + if (typeof multipliers[inputCurrency] !== 'string') { + throw new Error(inputCurrency + ' has no multipliers') + } + let depositAmountStr = tx.satoshiPrice?.toString() + if (typeof inputCurrency === 'string' && inputCurrency !== 'BTC') { + depositAmountStr = tx.receivedPaymentAltcoin?.toString() + } + if (depositAmountStr == null) { + throw new Error(`Missing depositAmount for tx: ${tx.orderId}`) + } + const depositAmount = safeParseFloat( + div(depositAmountStr, multipliers[inputCurrency], 8) + ) + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.orderId, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: inputCurrency, + depositAmount, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: tx.currency, + payoutAmount: parseInt(tx.value), + timestamp, + isoDate: new Date(tx.invoiceTime).toISOString(), + usdValue: tx.usdPrice, + rawTx + } + + return standardTx +} From 4c71b753f395d3a225343f28c41026c5118c99f7 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:05:51 -0800 Subject: [PATCH 06/35] Factor out `processBitsOfGoldTx` function --- src/partners/bitsofgold.ts | 88 +++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/src/partners/bitsofgold.ts b/src/partners/bitsofgold.ts index 3d148810..f3a39640 100644 --- a/src/partners/bitsofgold.ts +++ b/src/partners/bitsofgold.ts @@ -16,6 +16,7 @@ const asBogTx = asObject({ id: asString }) +type BogResult = ReturnType const asBogResult = asObject({ data: asArray(asUnknown) }) @@ -55,53 +56,21 @@ export async function queryBitsOfGold( 'x-api-key': apiKey } - let resultJSON + let result: BogResult try { - const result = await fetch(url, { method: 'GET', headers: headers }) - resultJSON = asBogResult(await result.json()) + const response = await fetch(url, { method: 'GET', headers: headers }) + result = asBogResult(await response.json()) } catch (e) { datelog(e) throw e } - const txs = resultJSON.data + const txs = result.data let latestTimeStamp = startDate.getTime() - for (const rawtx of txs) { - const tx = asBogTx(rawtx) - const data = tx.attributes - const date = new Date(data.timestamp) - const timestamp = date.getTime() - - let [depositCurrency, depositAmount, payoutCurrency, payoutAmount] = [ - data.coin_type, - data.coin_amount, - data.fiat_type, - data.fiat_amount - ] - if (tx.type.toLowerCase() === 'buy') { - depositCurrency = data.fiat_type - depositAmount = data.fiat_amount - payoutCurrency = data.coin_type - payoutAmount = data.fiat_amount - } - - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency, - depositAmount, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency, - payoutAmount, - timestamp: timestamp / 1000, - isoDate: date.toISOString(), - usdValue: -1, - rawTx: rawtx - } + for (const rawTx of txs) { + const standardTx: StandardTx = processBitsOfGoldTx(rawTx) + const timestamp = new Date(standardTx.isoDate).getTime() latestTimeStamp = latestTimeStamp > timestamp ? latestTimeStamp : timestamp - ssFormatTxs.push(ssTx) + ssFormatTxs.push(standardTx) } return { @@ -117,3 +86,42 @@ export const bitsofgold: PartnerPlugin = { pluginName: 'BitsOfGold', pluginId: 'bitsofgold' } + +export function processBitsOfGoldTx(rawTx: unknown): StandardTx { + const bogTx = asBogTx(rawTx) + const data = bogTx.attributes + const date = new Date(data.timestamp) + const timestamp = date.getTime() + + let [depositCurrency, depositAmount, payoutCurrency, payoutAmount] = [ + data.coin_type, + data.coin_amount, + data.fiat_type, + data.fiat_amount + ] + if (bogTx.type.toLowerCase() === 'buy') { + depositCurrency = data.fiat_type + depositAmount = data.fiat_amount + payoutCurrency = data.coin_type + payoutAmount = data.fiat_amount + } + + const standardTx: StandardTx = { + status: 'complete', + orderId: bogTx.id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency, + depositAmount, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency, + payoutAmount, + timestamp: timestamp / 1000, + isoDate: date.toISOString(), + usdValue: -1, + rawTx + } + + return standardTx +} From 4e58d5006393dc36831a117fc213f96761cd2d64 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:26:30 -0800 Subject: [PATCH 07/35] Factor out `processBityTx` function --- .vscode/settings.json | 2 +- src/partners/bity.ts | 49 ++++++++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f4199a36..f0c475d5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "cSpell.words": ["Bitrefill"] + "cSpell.words": ["Bitrefill", "Bity"] } diff --git a/src/partners/bity.ts b/src/partners/bity.ts index f9a05692..a040bdbd 100644 --- a/src/partners/bity.ts +++ b/src/partners/bity.ts @@ -25,7 +25,7 @@ const PAGE_SIZE = 100 export async function queryBity( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let tokenParams let credentials let authToken @@ -115,25 +115,9 @@ export async function queryBity( throw e } - for (const rawtx of monthlyTxs) { - const tx = asBityTx(rawtx) - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: tx.input.currency.toUpperCase(), - depositAmount: safeParseFloat(tx.input.amount), - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: tx.output.currency.toUpperCase(), - payoutAmount: safeParseFloat(tx.output.amount), - timestamp: Date.parse(tx.timestamp_created.concat('Z')) / 1000, - isoDate: new Date(tx.timestamp_created.concat('Z')).toISOString(), - usdValue: -1, - rawTx: rawtx - } - ssFormatTxs.push(ssTx) + for (const rawTx of monthlyTxs) { + const standardTx = processBityTx(rawTx) + standardTxs.push(standardTx) } moreCurrentMonthsTransactions = monthlyTxs.length === PAGE_SIZE page++ @@ -159,7 +143,7 @@ export async function queryBity( return { settings: { lastCheckedMonth: currentMonth, lastCheckedYear: currentYear }, - transactions: ssFormatTxs + transactions: standardTxs } } @@ -170,3 +154,26 @@ export const bity: PartnerPlugin = { pluginName: 'Bity', pluginId: 'bity' } + +export function processBityTx(rawTx: unknown): StandardTx { + const tx = asBityTx(rawTx) + + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: tx.input.currency.toUpperCase(), + depositAmount: safeParseFloat(tx.input.amount), + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: tx.output.currency.toUpperCase(), + payoutAmount: safeParseFloat(tx.output.amount), + timestamp: Date.parse(tx.timestamp_created.concat('Z')) / 1000, + isoDate: new Date(tx.timestamp_created.concat('Z')).toISOString(), + usdValue: -1, + rawTx + } + + return standardTx +} From 2174a466a5a21a817c80b03f987220404d7f37d1 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:30:10 -0800 Subject: [PATCH 08/35] Factor out `processChangeHeroTx` function --- src/partners/changehero.ts | 66 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/partners/changehero.ts b/src/partners/changehero.ts index 1d460ae1..7f333dd1 100644 --- a/src/partners/changehero.ts +++ b/src/partners/changehero.ts @@ -77,7 +77,7 @@ export async function queryChangeHero( return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -119,39 +119,18 @@ export async function queryChangeHero( break } for (const rawTx of txs) { - let tx: ChangeHeroTx - try { - tx = asChangeHeroTx(rawTx) - } catch (e) { - console.log(e) - throw e - } - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.id, - depositTxid: tx.payinHash, - depositAddress: tx.payinAddress, - depositCurrency: tx.currencyFrom.toUpperCase(), - depositAmount: safeParseFloat(tx.amountFrom), - payoutTxid: tx.payoutHash, - payoutAddress: tx.payoutAddress, - payoutCurrency: tx.currencyTo.toUpperCase(), - payoutAmount: safeParseFloat(tx.amountTo), - timestamp: tx.createdAt, - isoDate: smartIsoDateFromTimestamp(tx.createdAt).isoDate, - usdValue: -1, - rawTx - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + const standardTx = processChangeHeroTx(rawTx) + standardTxs.push(standardTx) + + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } - if (ssTx.isoDate < oldestIsoDate) { - oldestIsoDate = ssTx.isoDate + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - if (ssTx.isoDate < previousLatestIsoDate && !done) { + if (standardTx.isoDate < previousLatestIsoDate && !done) { datelog( - `ChangeHero done: date ${ssTx.isoDate} < ${previousLatestIsoDate}` + `ChangeHero done: date ${standardTx.isoDate} < ${previousLatestIsoDate}` ) done = true } @@ -166,7 +145,7 @@ export async function queryChangeHero( settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -178,3 +157,26 @@ export const changehero: PartnerPlugin = { pluginName: 'Changehero', pluginId: 'changehero' } + +export function processChangeHeroTx(rawTx: unknown): StandardTx { + const tx: ChangeHeroTx = asChangeHeroTx(rawTx) + + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: tx.id, + depositTxid: tx.payinHash, + depositAddress: tx.payinAddress, + depositCurrency: tx.currencyFrom.toUpperCase(), + depositAmount: safeParseFloat(tx.amountFrom), + payoutTxid: tx.payoutHash, + payoutAddress: tx.payoutAddress, + payoutCurrency: tx.currencyTo.toUpperCase(), + payoutAmount: safeParseFloat(tx.amountTo), + timestamp: tx.createdAt, + isoDate: smartIsoDateFromTimestamp(tx.createdAt).isoDate, + usdValue: -1, + rawTx + } + + return standardTx +} From f56cdeec16c7792bc86adda42482d184ed606fe7 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:33:02 -0800 Subject: [PATCH 09/35] Factor out `processChangellyTx` function --- .vscode/settings.json | 2 +- src/partners/changelly.ts | 58 ++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f0c475d5..5c463c78 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "cSpell.words": ["Bitrefill", "Bity"] + "cSpell.words": ["Bitrefill", "Bity", "Changelly"] } diff --git a/src/partners/changelly.ts b/src/partners/changelly.ts index faa66adb..5eeebc50 100644 --- a/src/partners/changelly.ts +++ b/src/partners/changelly.ts @@ -109,7 +109,7 @@ export async function queryChangelly( } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let newLatestTimeStamp = latestTimeStamp let done = false try { @@ -131,35 +131,20 @@ export async function queryChangelly( } for (const rawTx of txs) { if (asChangellyRawTx(rawTx).status === 'finished') { - const tx = asChangellyTx(rawTx) - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: tx.payinHash, - depositAddress: tx.payinAddress, - depositCurrency: tx.currencyFrom.toUpperCase(), - depositAmount: safeParseFloat(tx.amountFrom), - payoutTxid: tx.payoutHash, - payoutAddress: tx.payoutAddress, - payoutCurrency: tx.currencyTo.toUpperCase(), - payoutAmount: safeParseFloat(tx.amountTo), - timestamp: tx.createdAt, - isoDate: new Date(tx.createdAt * 1000).toISOString(), - usdValue: -1, - rawTx - } - ssFormatTxs.push(ssTx) - if (tx.createdAt > newLatestTimeStamp) { - newLatestTimeStamp = tx.createdAt + const standardTx = processChangellyTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.timestamp > newLatestTimeStamp) { + newLatestTimeStamp = standardTx.timestamp } if ( - tx.createdAt < latestTimeStamp - QUERY_LOOKBACK && + standardTx.timestamp < latestTimeStamp - QUERY_LOOKBACK && !done && !firstAttempt ) { datelog( - `Changelly done: date ${tx.createdAt} < ${latestTimeStamp - - QUERY_LOOKBACK}` + `Changelly done: date ${ + standardTx.timestamp + } < ${latestTimeStamp - QUERY_LOOKBACK}` ) done = true } @@ -172,7 +157,7 @@ export async function queryChangelly( } const out = { settings: { latestTimeStamp: newLatestTimeStamp, firstAttempt, offset }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -184,3 +169,26 @@ export const changelly: PartnerPlugin = { pluginName: 'Changelly', pluginId: 'changelly' } + +export function processChangellyTx(rawTx: unknown): StandardTx { + const tx = asChangellyTx(rawTx) + + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.id, + depositTxid: tx.payinHash, + depositAddress: tx.payinAddress, + depositCurrency: tx.currencyFrom.toUpperCase(), + depositAmount: safeParseFloat(tx.amountFrom), + payoutTxid: tx.payoutHash, + payoutAddress: tx.payoutAddress, + payoutCurrency: tx.currencyTo.toUpperCase(), + payoutAmount: safeParseFloat(tx.amountTo), + timestamp: tx.createdAt, + isoDate: new Date(tx.createdAt * 1000).toISOString(), + usdValue: -1, + rawTx + } + + return standardTx +} From 8836a0a9f676747bf1b6fcb50531c519f9f86b67 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:36:15 -0800 Subject: [PATCH 10/35] Factor out `processChangeNowTx` function --- src/partners/changenow.ts | 64 +++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/partners/changenow.ts b/src/partners/changenow.ts index 7c999ad8..233e9eb2 100644 --- a/src/partners/changenow.ts +++ b/src/partners/changenow.ts @@ -71,7 +71,7 @@ export const queryChangeNow = async ( return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -101,36 +101,10 @@ export const queryChangeNow = async ( break } for (const rawTx of txs) { - let tx: ChangeNowTx - try { - tx = asChangeNowTx(rawTx) - } catch (e) { - datelog(e) - throw e - } - const date = new Date( - tx.createdAt.endsWith('Z') ? tx.createdAt : tx.createdAt + 'Z' - ) - const timestamp = date.getTime() / 1000 - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.requestId, - depositTxid: tx.payin.hash, - depositAddress: tx.payin.address, - depositCurrency: tx.payin.currency.toUpperCase(), - depositAmount: tx.payin.amount ?? tx.payin.expectedAmount ?? 0, - payoutTxid: tx.payout.hash, - payoutAddress: tx.payout.address, - payoutCurrency: tx.payout.currency.toUpperCase(), - payoutAmount: tx.payout.amount ?? tx.payout.expectedAmount ?? 0, - timestamp, - isoDate: date.toISOString(), - usdValue: -1, - rawTx - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + const standardTx = processChangeNowTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } } datelog(`ChangeNow offset ${offset} latestIsoDate ${latestIsoDate}`) @@ -151,7 +125,7 @@ export const queryChangeNow = async ( } const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -163,3 +137,29 @@ export const changenow: PartnerPlugin = { pluginName: 'Changenow', pluginId: 'changenow' } + +export function processChangeNowTx(rawTx: unknown): StandardTx { + const tx: ChangeNowTx = asChangeNowTx(rawTx) + const date = new Date( + tx.createdAt.endsWith('Z') ? tx.createdAt : tx.createdAt + 'Z' + ) + const timestamp = date.getTime() / 1000 + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: tx.requestId, + depositTxid: tx.payin.hash, + depositAddress: tx.payin.address, + depositCurrency: tx.payin.currency.toUpperCase(), + depositAmount: tx.payin.amount ?? tx.payin.expectedAmount ?? 0, + payoutTxid: tx.payout.hash, + payoutAddress: tx.payout.address, + payoutCurrency: tx.payout.currency.toUpperCase(), + payoutAmount: tx.payout.amount ?? tx.payout.expectedAmount ?? 0, + timestamp, + isoDate: date.toISOString(), + usdValue: -1, + rawTx + } + + return standardTx +} From 665a2a1fbe15f537f9a0a1b6f3e694c9b7b1f08f Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:38:47 -0800 Subject: [PATCH 11/35] Factor out `processCoinSwitchTx` function --- src/partners/coinswitch.ts | 66 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/partners/coinswitch.ts b/src/partners/coinswitch.ts index be2d692d..a539ea12 100644 --- a/src/partners/coinswitch.ts +++ b/src/partners/coinswitch.ts @@ -12,7 +12,7 @@ import fetch from 'node-fetch' import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' import { datelog } from '../util' -const asCoinswitchTx = asObject({ +const asCoinSwitchTx = asObject({ status: asString, orderId: asString, inputTransactionHash: asEither(asString, asNull), @@ -37,7 +37,7 @@ const QUERY_LOOKBACK = 60 * 60 * 24 * 5 // 5 days export async function queryCoinSwitch( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let start = 0 let apiKey = '' let latestTimeStamp = 0 @@ -72,36 +72,13 @@ export async function queryCoinSwitch( } const txs = jsonObj.data.items for (const rawTx of txs) { - const tx = asCoinswitchTx(rawTx) - const depositTxid = - tx.inputTransactionHash === 'string' - ? tx.inputTransactionHash - : undefined - const payoutTxid = - tx.outputTransactionHash === 'string' - ? tx.outputTransactionHash - : undefined - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.orderId, - depositTxid, - depositAddress: tx.exchangeAddress.address, - depositCurrency: tx.depositCoin.toUpperCase(), - depositAmount: tx.depositCoinAmount, - payoutTxid, - payoutAddress: tx.destinationAddress.address, - payoutCurrency: tx.destinationCoin.toUpperCase(), - payoutAmount: tx.destinationCoinAmount, - timestamp: tx.createdAt / 1000, - isoDate: new Date(tx.createdAt).toISOString(), - usdValue: -1, - rawTx: rawTx + const standardTx = processCoinSwitchTx(rawTx) + standardTxs.push(standardTx) + const createdAt = standardTx.timestamp * 1000 + if (createdAt > newLatestTimeStamp) { + newLatestTimeStamp = createdAt } - ssFormatTxs.push(ssTx) - if (tx.createdAt > newLatestTimeStamp) { - newLatestTimeStamp = tx.createdAt - } - if (tx.createdAt < latestTimeStamp - QUERY_LOOKBACK) { + if (createdAt < latestTimeStamp - QUERY_LOOKBACK) { done = true } } @@ -113,7 +90,7 @@ export async function queryCoinSwitch( } const out: PluginResult = { settings: { latestTimeStamp: newLatestTimeStamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -125,3 +102,28 @@ export const coinswitch: PartnerPlugin = { pluginName: 'CoinSwitch', pluginId: 'coinswitch' } + +export function processCoinSwitchTx(rawTx: unknown): StandardTx { + const tx = asCoinSwitchTx(rawTx) + const depositTxid = + tx.inputTransactionHash === 'string' ? tx.inputTransactionHash : undefined + const payoutTxid = + tx.outputTransactionHash === 'string' ? tx.outputTransactionHash : undefined + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.orderId, + depositTxid, + depositAddress: tx.exchangeAddress.address, + depositCurrency: tx.depositCoin.toUpperCase(), + depositAmount: tx.depositCoinAmount, + payoutTxid, + payoutAddress: tx.destinationAddress.address, + payoutCurrency: tx.destinationCoin.toUpperCase(), + payoutAmount: tx.destinationCoinAmount, + timestamp: tx.createdAt / 1000, + isoDate: new Date(tx.createdAt).toISOString(), + usdValue: -1, + rawTx: rawTx + } + return standardTx +} From c9421a2ad5155863f3fdde37cbe3769f7ea5cdd3 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:40:55 -0800 Subject: [PATCH 12/35] Factor out `processExolixTx` function --- src/partners/exolix.ts | 70 +++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/partners/exolix.ts b/src/partners/exolix.ts index 94397601..60c839a5 100644 --- a/src/partners/exolix.ts +++ b/src/partners/exolix.ts @@ -97,7 +97,7 @@ export async function queryExolix( return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -125,41 +125,18 @@ export async function queryExolix( const txs = result.data for (const rawTx of txs) { - let tx: ExolixTx - try { - tx = asExolixTx(rawTx) - } catch (e) { - datelog(e) - throw e + const standardTx = processExolixTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } - const dateInMillis = Date.parse(tx.createdAt) - const { isoDate, timestamp } = smartIsoDateFromTimestamp(dateInMillis) - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.id, - depositTxid: tx.hashIn?.hash ?? '', - depositAddress: tx.depositAddress, - depositCurrency: tx.coinFrom.coinCode, - depositAmount: tx.amount, - payoutTxid: tx.hashOut?.hash ?? '', - payoutAddress: tx.withdrawalAddress, - payoutCurrency: tx.coinTo.coinCode, - payoutAmount: tx.amountTo, - timestamp, - isoDate, - usdValue: -1, - rawTx + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - - ssFormatTxs.push(ssTx) - if (isoDate > latestIsoDate) { - latestIsoDate = isoDate - } - if (isoDate < oldestIsoDate) { - oldestIsoDate = isoDate - } - if (isoDate < previousLatestIsoDate && !done) { - datelog(`Exolix done: date ${isoDate} < ${previousLatestIsoDate}`) + if (standardTx.isoDate < previousLatestIsoDate && !done) { + datelog( + `Exolix done: date ${standardTx.isoDate} < ${previousLatestIsoDate}` + ) done = true } } @@ -174,7 +151,7 @@ export async function queryExolix( const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -186,3 +163,26 @@ export const exolix: PartnerPlugin = { pluginName: 'Exolix', pluginId: 'exolix' } + +export function processExolixTx(rawTx: unknown): StandardTx { + const tx: ExolixTx = asExolixTx(rawTx) + const dateInMillis = Date.parse(tx.createdAt) + const { isoDate, timestamp } = smartIsoDateFromTimestamp(dateInMillis) + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: tx.id, + depositTxid: tx.hashIn?.hash ?? '', + depositAddress: tx.depositAddress, + depositCurrency: tx.coinFrom.coinCode, + depositAmount: tx.amount, + payoutTxid: tx.hashOut?.hash ?? '', + payoutAddress: tx.withdrawalAddress, + payoutCurrency: tx.coinTo.coinCode, + payoutAmount: tx.amountTo, + timestamp, + isoDate, + usdValue: -1, + rawTx + } + return standardTx +} From 8cc5299c131a5db02352259f3342cb23d36b674f Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:42:49 -0800 Subject: [PATCH 13/35] Factor out `processFaastTx` function --- .vscode/settings.json | 2 +- src/partners/faast.ts | 55 +++++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5c463c78..fe14639c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "cSpell.words": ["Bitrefill", "Bity", "Changelly"] + "cSpell.words": ["Bitrefill", "Bity", "Changelly", "Faast"] } diff --git a/src/partners/faast.ts b/src/partners/faast.ts index 17314bd3..9036129b 100644 --- a/src/partners/faast.ts +++ b/src/partners/faast.ts @@ -33,7 +33,7 @@ export async function queryFaast( pluginParams: PluginParams ): Promise { let page = 1 - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let signature = '' let latestTimeStamp = 0 if (typeof pluginParams.settings.latestTimeStamp === 'number') { @@ -75,30 +75,12 @@ export async function queryFaast( const txs = jsonObj.orders for (const rawtx of txs) { if (asRawFaastTx(rawtx).status === 'complete') { - const tx = asFaastTx(rawtx) - const date = new Date(tx.created_at) - const timestamp = date.getTime() / 1000 - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.swap_id, - depositTxid: undefined, - depositAddress: tx.deposit_address, - depositCurrency: tx.deposit_currency.toUpperCase(), - depositAmount: tx.amount_deposited, - payoutTxid: tx.transaction_id, - payoutAddress: tx.withdrawal_address, - payoutCurrency: tx.withdrawal_currency.toUpperCase(), - payoutAmount: tx.amount_withdrawn, - timestamp, - isoDate: tx.created_at, - usdValue: -1, - rawTx: rawtx + const standardTx = processFaastTx(rawtx) + standardTxs.push(standardTx) + if (standardTx.timestamp > newLatestTimeStamp) { + newLatestTimeStamp = standardTx.timestamp } - ssFormatTxs.push(ssTx) - if (timestamp > newLatestTimeStamp) { - newLatestTimeStamp = timestamp - } - if (timestamp < latestTimeStamp - QUERY_LOOKBACK) { + if (standardTx.timestamp < latestTimeStamp - QUERY_LOOKBACK) { done = true } } @@ -110,7 +92,7 @@ export async function queryFaast( } const out: PluginResult = { settings: { latestTimeStamp: newLatestTimeStamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -122,3 +104,26 @@ export const faast: PartnerPlugin = { pluginName: 'Faast', pluginId: 'faast' } + +export function processFaastTx(rawTx: unknown): StandardTx { + const tx = asFaastTx(rawTx) + const date = new Date(tx.created_at) + const timestamp = date.getTime() / 1000 + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.swap_id, + depositTxid: undefined, + depositAddress: tx.deposit_address, + depositCurrency: tx.deposit_currency.toUpperCase(), + depositAmount: tx.amount_deposited, + payoutTxid: tx.transaction_id, + payoutAddress: tx.withdrawal_address, + payoutCurrency: tx.withdrawal_currency.toUpperCase(), + payoutAmount: tx.amount_withdrawn, + timestamp, + isoDate: tx.created_at, + usdValue: -1, + rawTx + } + return standardTx +} From 67d38b2d15df6a49528083e94d5250be461f2ace Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:45:19 -0800 Subject: [PATCH 14/35] Factor out `processFoxExchangeTx` function --- src/partners/foxExchange.ts | 68 ++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/partners/foxExchange.ts b/src/partners/foxExchange.ts index c9c67d5b..c77ba9c5 100644 --- a/src/partners/foxExchange.ts +++ b/src/partners/foxExchange.ts @@ -31,7 +31,7 @@ const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 3 // 3 days ago export async function queryFoxExchange( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let apiKey let secretToken let lastCheckedTimestamp @@ -80,36 +80,15 @@ export async function queryFoxExchange( throw e } - for (const rawtx of txs.data.items) { - if (asFoxExchangeRawTx(rawtx).status === 'complete') { - let tx - try { - tx = asFoxExchangeTx(rawtx) - } catch (e) { - datelog(e) - throw e + for (const rawTx of txs.data.items) { + if (asFoxExchangeRawTx(rawTx).status === 'complete') { + const standardTx = processFoxExchangeTx(rawTx) + standardTxs.push(standardTx) + const createdAt = standardTx.timestamp * 1000 + if (createdAt > newestTimestamp) { + newestTimestamp = createdAt } - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.orderId, - depositTxid: undefined, - depositAddress: tx.exchangeAddress.address, - depositCurrency: tx.depositCoin.toUpperCase(), - depositAmount: tx.depositCoinAmount, - payoutTxid: tx.outputTransactionHash, - payoutAddress: tx.destinationAddress.address, - payoutCurrency: tx.destinationCoin.toUpperCase(), - payoutAmount: tx.destinationCoinAmount, - timestamp: tx.createdAt / 1000, - isoDate: new Date(tx.createdAt).toISOString(), - usdValue: -1, - rawTx: rawtx - } - ssFormatTxs.push(ssTx) - if (tx.createdAt > newestTimestamp) { - newestTimestamp = tx.createdAt - } - if (lastCheckedTimestamp > tx.createdAt) { + if (lastCheckedTimestamp > createdAt) { done = true } } @@ -125,7 +104,7 @@ export async function queryFoxExchange( const out: PluginResult = { settings: { lastCheckedTimestamp: newestTimestamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -137,3 +116,30 @@ export const foxExchange: PartnerPlugin = { pluginName: 'FoxExchange', pluginId: 'foxExchange' } + +export function processFoxExchangeTx(rawTx: unknown): StandardTx { + let tx + try { + tx = asFoxExchangeTx(rawTx) + } catch (e) { + datelog(e) + throw e + } + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.orderId, + depositTxid: undefined, + depositAddress: tx.exchangeAddress.address, + depositCurrency: tx.depositCoin.toUpperCase(), + depositAmount: tx.depositCoinAmount, + payoutTxid: tx.outputTransactionHash, + payoutAddress: tx.destinationAddress.address, + payoutCurrency: tx.destinationCoin.toUpperCase(), + payoutAmount: tx.destinationCoinAmount, + timestamp: tx.createdAt / 1000, + isoDate: new Date(tx.createdAt).toISOString(), + usdValue: -1, + rawTx + } + return standardTx +} From b36286e79d7c3ddcf317b07fb9e2966db1833536 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 15:56:53 -0800 Subject: [PATCH 15/35] Factor out `processGodexTx` function --- .vscode/settings.json | 2 +- src/partners/godex.ts | 71 ++++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index fe14639c..abbc1465 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "cSpell.words": ["Bitrefill", "Bity", "Changelly", "Faast"] + "cSpell.words": ["Bitrefill", "Bity", "Changelly", "Faast", "godex"] } diff --git a/src/partners/godex.ts b/src/partners/godex.ts index 825cd79f..3b396e9e 100644 --- a/src/partners/godex.ts +++ b/src/partners/godex.ts @@ -86,7 +86,7 @@ export async function queryGodex( return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -106,41 +106,19 @@ export async function queryGodex( const resultJSON = await result.json() const txs = asGodexResult(resultJSON) - for (const rawtx of txs) { - let tx: GodexTx - try { - tx = asGodexTx(rawtx) - } catch (e) { - datelog(e) - throw e + for (const rawTx of txs) { + const standardTx = processGodexTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } - const ts = parseInt(tx.created_at) - const { isoDate, timestamp } = smartIsoDateFromTimestamp(ts) - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.transaction_id, - depositTxid: tx.hash_in, - depositAddress: tx.deposit, - depositCurrency: tx.coin_from.toUpperCase(), - depositAmount: safeParseFloat(tx.deposit_amount), - payoutTxid: undefined, - payoutAddress: tx.withdrawal, - payoutCurrency: tx.coin_to.toUpperCase(), - payoutAmount: safeParseFloat(tx.withdrawal_amount), - timestamp, - isoDate, - usdValue: -1, - rawTx: rawtx + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - ssFormatTxs.push(ssTx) - if (isoDate > latestIsoDate) { - latestIsoDate = isoDate - } - if (isoDate < oldestIsoDate) { - oldestIsoDate = isoDate - } - if (isoDate < previousLatestIsoDate && !done) { - datelog(`Godex done: date ${isoDate} < ${previousLatestIsoDate}`) + if (standardTx.isoDate < previousLatestIsoDate && !done) { + datelog( + `Godex done: date ${standardTx.isoDate} < ${previousLatestIsoDate}` + ) done = true } } @@ -158,7 +136,7 @@ export async function queryGodex( } const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -169,3 +147,26 @@ export const godex: PartnerPlugin = { pluginName: 'Godex', pluginId: 'godex' } + +export function processGodexTx(rawTx: unknown): StandardTx { + const tx: GodexTx = asGodexTx(rawTx) + const ts = parseInt(tx.created_at) + const { isoDate, timestamp } = smartIsoDateFromTimestamp(ts) + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: tx.transaction_id, + depositTxid: tx.hash_in, + depositAddress: tx.deposit, + depositCurrency: tx.coin_from.toUpperCase(), + depositAmount: safeParseFloat(tx.deposit_amount), + payoutTxid: undefined, + payoutAddress: tx.withdrawal, + payoutCurrency: tx.coin_to.toUpperCase(), + payoutAmount: safeParseFloat(tx.withdrawal_amount), + timestamp, + isoDate, + usdValue: -1, + rawTx: rawTx + } + return standardTx +} From 70f506261f9e7b5cc8ffc0d9517e8f14f59cc98f Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:02:46 -0800 Subject: [PATCH 16/35] Factor out `processIoniaGiftCardsTx` function --- src/partners/ioniagiftcard.ts | 68 ++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/partners/ioniagiftcard.ts b/src/partners/ioniagiftcard.ts index 3522a8fc..923c45c2 100644 --- a/src/partners/ioniagiftcard.ts +++ b/src/partners/ioniagiftcard.ts @@ -24,7 +24,10 @@ const asIoniaTx = asObject({ Id: asNumber, CreatedDate: asString, GiftCardFaceValue: asNumber, - USDPaidByCustomer: asNumber, + USDPaidByCustomer: asNumber +}) + +const asPreIoniaTx = asObject({ MerchantName: asString }) @@ -46,7 +49,7 @@ const statusMap: { [key in IoniaStatus]: Status } = { other: 'complete' } -export const queryIonia = async ( +export const queryIoniaGiftCards = async ( pluginParams: PluginParams ): Promise => { const { settings, apiKeys } = asStandardPluginParams(pluginParams) @@ -57,7 +60,7 @@ export const queryIonia = async ( return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -89,36 +92,13 @@ export const queryIonia = async ( const txs = asIoniaResult(result).Data.Transactions for (const rawTx of txs) { - let tx: IoniaTx - try { - tx = asIoniaTx(rawTx) - } catch (e) { - datelog(e) - throw e - } - if (tx.MerchantName === 'Visa eReward Card') { + if (asPreIoniaTx(rawTx).MerchantName === 'Visa eReward Card') { continue } - const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.CreatedDate) - const ssTx: StandardTx = { - status: statusMap.complete, - orderId: tx.Id.toString(), - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: 'USD', - depositAmount: tx.USDPaidByCustomer, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: 'USD', - payoutAmount: tx.GiftCardFaceValue, - timestamp, - isoDate, - usdValue: tx.GiftCardFaceValue, - rawTx - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + const standardTx = processIoniaGiftCardsTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } } datelog(`IoniaGiftCards latestIsoDate ${latestIsoDate}`) @@ -142,15 +122,37 @@ export const queryIonia = async ( } const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } export const ioniaGiftCards: PartnerPlugin = { // queryFunc will take PluginSettings as arg and return PluginResult - queryFunc: queryIonia, + queryFunc: queryIoniaGiftCards, // results in a PluginResult pluginName: 'Ionia Gift Cards', pluginId: 'ioniagiftcards' } + +export function processIoniaGiftCardsTx(rawTx: unknown): StandardTx { + const tx: IoniaTx = asIoniaTx(rawTx) + const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.CreatedDate) + const standardTx: StandardTx = { + status: statusMap.complete, + orderId: tx.Id.toString(), + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: 'USD', + depositAmount: tx.USDPaidByCustomer, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: 'USD', + payoutAmount: tx.GiftCardFaceValue, + timestamp, + isoDate, + usdValue: tx.GiftCardFaceValue, + rawTx + } + return standardTx +} From 876c834a824623416f17645e1b23df0caa9f084d Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:06:59 -0800 Subject: [PATCH 17/35] Factor out `processIoniaVisaRewardsTx` function --- src/partners/ioniavisarewards.ts | 68 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/partners/ioniavisarewards.ts b/src/partners/ioniavisarewards.ts index d27fa9f1..3dd94ff9 100644 --- a/src/partners/ioniavisarewards.ts +++ b/src/partners/ioniavisarewards.ts @@ -24,7 +24,10 @@ const asIoniaTx = asObject({ Id: asNumber, CreatedDate: asString, GiftCardFaceValue: asNumber, - USDPaidByCustomer: asNumber, + USDPaidByCustomer: asNumber +}) + +const asPreIoniaTx = asObject({ MerchantName: asString }) @@ -46,7 +49,7 @@ const statusMap: { [key in IoniaStatus]: Status } = { other: 'complete' } -export const queryIonia = async ( +export const queryIoniaVisaRewards = async ( pluginParams: PluginParams ): Promise => { const { settings, apiKeys } = asStandardPluginParams(pluginParams) @@ -57,7 +60,7 @@ export const queryIonia = async ( return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -89,36 +92,13 @@ export const queryIonia = async ( const txs = asIoniaResult(result).Data.Transactions for (const rawTx of txs) { - let tx: IoniaTx - try { - tx = asIoniaTx(rawTx) - } catch (e) { - datelog(e) - throw e - } - if (tx.MerchantName !== 'Visa eReward Card') { + if (asPreIoniaTx(rawTx).MerchantName !== 'Visa eReward Card') { continue } - const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.CreatedDate) - const ssTx: StandardTx = { - status: statusMap.complete, - orderId: tx.Id.toString(), - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: 'USD', - depositAmount: tx.USDPaidByCustomer, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: 'USD', - payoutAmount: tx.GiftCardFaceValue, - timestamp, - isoDate, - usdValue: tx.GiftCardFaceValue, - rawTx - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + const standardTx = processIoniaVisaRewardsTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } } datelog(`IoniaVisaRewards latestIsoDate ${latestIsoDate}`) @@ -142,15 +122,37 @@ export const queryIonia = async ( } const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } export const ioniaVisaRewards: PartnerPlugin = { // queryFunc will take PluginSettings as arg and return PluginResult - queryFunc: queryIonia, + queryFunc: queryIoniaVisaRewards, // results in a PluginResult pluginName: 'Ionia Visa Rewards', pluginId: 'ioniavisarewards' } + +export function processIoniaVisaRewardsTx(rawTx: unknown): StandardTx { + const tx: IoniaTx = asIoniaTx(rawTx) + const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.CreatedDate) + const standardTx: StandardTx = { + status: statusMap.complete, + orderId: tx.Id.toString(), + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: 'USD', + depositAmount: tx.USDPaidByCustomer, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: 'USD', + payoutAmount: tx.GiftCardFaceValue, + timestamp, + isoDate, + usdValue: tx.GiftCardFaceValue, + rawTx + } + return standardTx +} From ee2ad54e2fe9f04ac1d287978f90d993accd182f Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:13:46 -0800 Subject: [PATCH 18/35] Factor out `processKadoTx` function --- .vscode/settings.json | 2 +- src/partners/kado.ts | 115 +++++++++++++++++++----------------------- 2 files changed, 52 insertions(+), 65 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index abbc1465..5fd2fa53 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "cSpell.words": ["Bitrefill", "Bity", "Changelly", "Faast", "godex"] + "cSpell.words": ["Bitrefill", "Bity", "Changelly", "Faast", "godex", "Kado"] } diff --git a/src/partners/kado.ts b/src/partners/kado.ts index 4a4c7213..a4d3a98b 100644 --- a/src/partners/kado.ts +++ b/src/partners/kado.ts @@ -2,10 +2,9 @@ import { asArray, asBoolean, asDate, - asMaybe, + asEither, asNumber, asObject, - asOptional, asString, asValue } from 'cleaners' @@ -15,8 +14,7 @@ import { PartnerPlugin, PluginParams, PluginResult, - StandardTx, - Status + StandardTx } from '../types' import { datelog, retryFetch, smartIsoDateFromTimestamp, snooze } from '../util' @@ -47,6 +45,8 @@ const asOffRampTx = asObject({ // disburseMethod: asString }) +const asKadoTx = asEither(asOnRampTx, asOffRampTx) + // Define cleaner for the main data structure const asResponse = asObject({ success: asBoolean, @@ -72,7 +72,7 @@ export async function queryKado( latestIsoDate = new Date('2024-01-01T00:00:00.000Z').toISOString() } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let retry = 0 const url = `https://api.kado.money/v2/organizations/${apiKey}/orders` @@ -85,64 +85,9 @@ export async function queryKado( const jsonObj = await response.json() const transferResults = asResponse(jsonObj) const { onRamps, offRamps } = transferResults.data - for (const tx of onRamps) { - const { - _id, - createdAt, - cryptoCurrency, - paidAmountUsd, - receiveUnitCount, - walletAddress - } = tx - const { isoDate, timestamp } = smartIsoDateFromTimestamp( - createdAt.toISOString() - ) - const ssTx: StandardTx = { - status: 'complete', - orderId: _id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: 'USD', - depositAmount: paidAmountUsd, - payoutTxid: undefined, - payoutAddress: walletAddress, - payoutCurrency: cryptoCurrency, - payoutAmount: receiveUnitCount, - timestamp, - isoDate, - usdValue: paidAmountUsd, - rawTx: tx - } - ssFormatTxs.push(ssTx) - } - for (const tx of offRamps) { - const { - _id, - createdAt, - cryptoCurrency, - depositUnitCount, - receiveUsd - } = tx - const { isoDate, timestamp } = smartIsoDateFromTimestamp( - createdAt.toISOString() - ) - const ssTx: StandardTx = { - status: 'complete', - orderId: _id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: cryptoCurrency, - depositAmount: depositUnitCount, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: 'USD', - payoutAmount: receiveUsd, - timestamp, - isoDate, - usdValue: receiveUsd, - rawTx: tx - } - ssFormatTxs.push(ssTx) + for (const rawTx of onRamps) { + const standardTx: StandardTx = processKadoTx(rawTx) + standardTxs.push(standardTx) } datelog(`Kado latestIsoDate:${latestIsoDate}`) retry = 0 @@ -161,7 +106,7 @@ export async function queryKado( const out = { settings: {}, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -171,3 +116,45 @@ export const kado: PartnerPlugin = { pluginName: 'Kado', pluginId: 'kado' } + +export function processKadoTx(rawTx: unknown): StandardTx { + const tx = asKadoTx(rawTx) + const { isoDate, timestamp } = smartIsoDateFromTimestamp( + tx.createdAt.toISOString() + ) + if ('paidAmountUsd' in tx) { + return { + status: 'complete', + orderId: tx._id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: 'USD', + depositAmount: tx.paidAmountUsd, + payoutTxid: undefined, + payoutAddress: tx.walletAddress, + payoutCurrency: tx.cryptoCurrency, + payoutAmount: tx.receiveUnitCount, + timestamp, + isoDate, + usdValue: tx.paidAmountUsd, + rawTx: tx + } + } else { + return { + status: 'complete', + orderId: tx._id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: tx.cryptoCurrency, + depositAmount: tx.depositUnitCount, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: 'USD', + payoutAmount: tx.receiveUsd, + timestamp, + isoDate, + usdValue: tx.receiveUsd, + rawTx: tx + } + } +} From 9d428114cb901fc180726c21e1899b28c803c08d Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:15:25 -0800 Subject: [PATCH 19/35] Factor out `processLetsExchangeTx` function --- src/partners/letsexchange.ts | 69 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/partners/letsexchange.ts b/src/partners/letsexchange.ts index 16980943..bb125836 100644 --- a/src/partners/letsexchange.ts +++ b/src/partners/letsexchange.ts @@ -90,7 +90,7 @@ export async function queryLetsExchange( return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -116,40 +116,18 @@ export async function queryLetsExchange( const { data: txs } = asLetsExchangeResult(resultJSON) for (const rawTx of txs) { - let tx: LetsExchangeTx - try { - tx = asLetsExchangeTx(rawTx) - } catch (e) { - datelog(e) - throw e + const standardTx = processLetsExchangeTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } - const ts = parseInt(tx.created_at) - const { isoDate, timestamp } = smartIsoDateFromTimestamp(ts) - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.transaction_id, - depositTxid: tx.hash_in, - depositAddress: tx.deposit, - depositCurrency: tx.coin_from.toUpperCase(), - depositAmount: safeParseFloat(tx.deposit_amount), - payoutTxid: undefined, - payoutAddress: tx.withdrawal, - payoutCurrency: tx.coin_to.toUpperCase(), - payoutAmount: safeParseFloat(tx.withdrawal_amount), - timestamp, - isoDate, - usdValue: -1, - rawTx + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - ssFormatTxs.push(ssTx) - if (isoDate > latestIsoDate) { - latestIsoDate = isoDate - } - if (isoDate < oldestIsoDate) { - oldestIsoDate = isoDate - } - if (isoDate < previousLatestIsoDate && !done) { - datelog(`Godex done: date ${isoDate} < ${previousLatestIsoDate}`) + if (standardTx.isoDate < previousLatestIsoDate && !done) { + datelog( + `Godex done: date ${standardTx.isoDate} < ${previousLatestIsoDate}` + ) done = true } } @@ -168,7 +146,7 @@ export async function queryLetsExchange( const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -179,3 +157,26 @@ export const letsexchange: PartnerPlugin = { pluginName: 'LetsExchange', pluginId: 'letsexchange' } + +export function processLetsExchangeTx(rawTx: unknown): StandardTx { + const tx: LetsExchangeTx = asLetsExchangeTx(rawTx) + const ts = parseInt(tx.created_at) + const { isoDate, timestamp } = smartIsoDateFromTimestamp(ts) + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: tx.transaction_id, + depositTxid: tx.hash_in, + depositAddress: tx.deposit, + depositCurrency: tx.coin_from.toUpperCase(), + depositAmount: safeParseFloat(tx.deposit_amount), + payoutTxid: undefined, + payoutAddress: tx.withdrawal, + payoutCurrency: tx.coin_to.toUpperCase(), + payoutAmount: safeParseFloat(tx.withdrawal_amount), + timestamp, + isoDate, + usdValue: -1, + rawTx + } + return standardTx +} From 054a487e57e544303a8ba31155379e231836a4ed Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:20:30 -0800 Subject: [PATCH 20/35] Factor out `processLibertyxTx` function --- .vscode/settings.json | 10 ++++++- src/partners/libertyx.ts | 57 +++++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5fd2fa53..7a6b4d0e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,11 @@ { - "cSpell.words": ["Bitrefill", "Bity", "Changelly", "Faast", "godex", "Kado"] + "cSpell.words": [ + "Bitrefill", + "Bity", + "Changelly", + "Faast", + "godex", + "Kado", + "Libertyx" + ] } diff --git a/src/partners/libertyx.ts b/src/partners/libertyx.ts index fd56dbaf..aaf11d4a 100644 --- a/src/partners/libertyx.ts +++ b/src/partners/libertyx.ts @@ -12,9 +12,12 @@ import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' import { datelog } from '../util' const asLibertyxTx = asObject({ - all_transactions_usd_sum: asOptional(asNumber), + all_transactions_usd_sum: asNumber, date_us_eastern: asString }) +const asPreLibertyxTx = asObject({ + all_transactions_usd_sum: asOptional(asNumber) +}) const asLibertyxResult = asObject({ stats: asArray(asUnknown) }) @@ -23,7 +26,7 @@ const INCOMPLETE_DAY_RANGE = 3 export async function queryLibertyx( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let apiKey let result if (typeof pluginParams.apiKeys.apiKey === 'string') { @@ -53,34 +56,17 @@ export async function queryLibertyx( continue } const rawTx = result.stats[index] - const tx = asLibertyxTx(rawTx) - if (typeof tx.all_transactions_usd_sum !== 'number') { + const preTx = asPreLibertyxTx(rawTx) + if (typeof preTx.all_transactions_usd_sum !== 'number') { continue } - const date = new Date(tx.date_us_eastern) - const timestamp = date.getTime() / 1000 - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.date_us_eastern, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: 'USD', - depositAmount: tx.all_transactions_usd_sum, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: 'BTC', - payoutAmount: 0, - timestamp: timestamp, - isoDate: date.toISOString(), - usdValue: tx.all_transactions_usd_sum, - rawTx - } - ssFormatTxs.push(ssTx) + const standardTx = processLibertyxTx(rawTx) + standardTxs.push(standardTx) } const out: PluginResult = { settings: {}, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -92,3 +78,26 @@ export const libertyx: PartnerPlugin = { pluginName: 'Libertyx', pluginId: 'libertyx' } + +export function processLibertyxTx(rawTx: unknown): StandardTx { + const tx = asLibertyxTx(rawTx) + const date = new Date(tx.date_us_eastern) + const timestamp = date.getTime() / 1000 + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.date_us_eastern, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: 'USD', + depositAmount: tx.all_transactions_usd_sum, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: 'BTC', + payoutAmount: 0, + timestamp: timestamp, + isoDate: date.toISOString(), + usdValue: tx.all_transactions_usd_sum, + rawTx + } + return standardTx +} From 821e148c38627ce7ac556b606bf9fba63ee57ad6 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:22:30 -0800 Subject: [PATCH 21/35] Factor out `processLifiTx` function --- .vscode/settings.json | 3 +- src/partners/lifi.ts | 87 ++++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a6b4d0e..4205ec68 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "Faast", "godex", "Kado", - "Libertyx" + "Libertyx", + "Lifi" ] } diff --git a/src/partners/lifi.ts b/src/partners/lifi.ts index b3ae4b31..1ae8607f 100644 --- a/src/partners/lifi.ts +++ b/src/partners/lifi.ts @@ -5,6 +5,7 @@ import { asObject, asOptional, asString, + asUnknown, asValue } from 'cleaners' @@ -66,7 +67,7 @@ const asTransfer = asObject({ // Define the cleaner for the whole JSON const asTransfersResult = asObject({ - transfers: asArray(asTransfer) + transfers: asArray(asUnknown) }) type PartnerStatuses = ReturnType @@ -94,7 +95,7 @@ export async function queryLifi( let lastCheckedTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (lastCheckedTimestamp < 0) lastCheckedTimestamp = 0 - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let retry = 0 let startTime = lastCheckedTimestamp @@ -114,45 +115,11 @@ export async function queryLifi( } const jsonObj = await response.json() const transferResults = asTransfersResult(jsonObj) - for (const tx of transferResults.transfers) { - const txTimestamp = tx.receiving.timestamp ?? tx.sending.timestamp ?? 0 - if (txTimestamp === 0) { - throw new Error('No timestamp') - } - const { isoDate, timestamp } = smartIsoDateFromTimestamp(txTimestamp) - - const depositToken = tx.sending.token ?? tx.sending.gasToken - const payoutToken = tx.receiving.token ?? tx.receiving.gasToken - if (depositToken == null || payoutToken == null) { - throw new Error('Missing token details') - } - const depositAmount = - Number(tx.sending.value) / 10 ** depositToken.decimals - - const payoutAmount = - Number(tx.receiving.value) / 10 ** payoutToken.decimals - - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.sending.txHash, - depositTxid: tx.sending.txHash, - depositAddress: undefined, - depositCurrency: depositToken.symbol, - depositAmount, - payoutTxid: undefined, - payoutAddress: tx.toAddress, - payoutCurrency: payoutToken.symbol, - payoutAmount, - timestamp, - isoDate, - usdValue: Number( - tx.receiving.amountUSD ?? tx.sending.amountUSD ?? '-1' - ), - rawTx: tx - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + for (const rawTx of transferResults.transfers) { + const standardTx = processLifiTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } } const endDate = new Date(endTime) @@ -181,7 +148,7 @@ export async function queryLifi( const out = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -191,3 +158,39 @@ export const lifi: PartnerPlugin = { pluginName: 'Li.Fi', pluginId: 'lifi' } + +export function processLifiTx(rawTx: unknown): StandardTx { + const tx = asTransfer(rawTx) + const txTimestamp = tx.receiving.timestamp ?? tx.sending.timestamp ?? 0 + if (txTimestamp === 0) { + throw new Error('No timestamp') + } + const { isoDate, timestamp } = smartIsoDateFromTimestamp(txTimestamp) + + const depositToken = tx.sending.token ?? tx.sending.gasToken + const payoutToken = tx.receiving.token ?? tx.receiving.gasToken + if (depositToken == null || payoutToken == null) { + throw new Error('Missing token details') + } + const depositAmount = Number(tx.sending.value) / 10 ** depositToken.decimals + + const payoutAmount = Number(tx.receiving.value) / 10 ** payoutToken.decimals + + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: tx.sending.txHash, + depositTxid: tx.sending.txHash, + depositAddress: undefined, + depositCurrency: depositToken.symbol, + depositAmount, + payoutTxid: undefined, + payoutAddress: tx.toAddress, + payoutCurrency: payoutToken.symbol, + payoutAmount, + timestamp, + isoDate, + usdValue: Number(tx.receiving.amountUSD ?? tx.sending.amountUSD ?? '-1'), + rawTx + } + return standardTx +} From 4731352c0dc377cd939ef51b50f1e755d0ed3e62 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:24:39 -0800 Subject: [PATCH 22/35] Factor out `processMoonpayTx` function --- src/partners/moonpay.ts | 65 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/partners/moonpay.ts b/src/partners/moonpay.ts index be02d588..110644d7 100644 --- a/src/partners/moonpay.ts +++ b/src/partners/moonpay.ts @@ -26,7 +26,7 @@ const asMoonpayTx = asObject({ type MoonpayTx = ReturnType -const asMoonpayRawTx = asObject({ +const asPreMoonpayTx = asObject({ status: asString }) @@ -38,7 +38,7 @@ const PER_REQUEST_LIMIT = 50 export async function queryMoonpay( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let headers let latestTimestamp = 0 @@ -74,36 +74,12 @@ export async function queryMoonpay( // cryptoTransactionId is a duplicate among other transactions sometimes // in bulk update it throws an error for document update conflict because of this. - for (const rawtx of txs) { - if (asMoonpayRawTx(rawtx).status === 'completed') { - let tx: MoonpayTx - try { - tx = asMoonpayTx(rawtx) - } catch (e) { - datelog(e) - datelog(rawtx) - throw e - } - - const date = new Date(tx.createdAt) - const timestamp = date.getTime() - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: tx.baseCurrency.code.toUpperCase(), - depositAmount: tx.baseCurrencyAmount, - payoutTxid: tx.cryptoTransactionId, - payoutAddress: tx.walletAddress, - payoutCurrency: tx.currency.code.toUpperCase(), - payoutAmount: tx.quoteCurrencyAmount, - timestamp: timestamp / 1000, - isoDate: tx.createdAt, - usdValue: -1, - rawTx: rawtx - } - ssFormatTxs.push(ssTx) + for (const rawTx of txs) { + const preTx = asPreMoonpayTx(rawTx) + if (preTx.status === 'completed') { + const standardTx = processMoonpayTx(rawTx) + standardTxs.push(standardTx) + const timestamp = standardTx.timestamp * 1000 done = latestTimestamp > timestamp || txs.length < PER_REQUEST_LIMIT newestTimestamp = newestTimestamp > timestamp ? newestTimestamp : timestamp @@ -115,7 +91,7 @@ export async function queryMoonpay( const out: PluginResult = { settings: { latestTimestamp: newestTimestamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -127,3 +103,26 @@ export const moonpay: PartnerPlugin = { pluginName: 'Moonpay', pluginId: 'moonpay' } + +export function processMoonpayTx(rawTx: unknown): StandardTx { + const tx: MoonpayTx = asMoonpayTx(rawTx) + const date = new Date(tx.createdAt) + const timestamp = date.getTime() + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: tx.baseCurrency.code.toUpperCase(), + depositAmount: tx.baseCurrencyAmount, + payoutTxid: tx.cryptoTransactionId, + payoutAddress: tx.walletAddress, + payoutCurrency: tx.currency.code.toUpperCase(), + payoutAmount: tx.quoteCurrencyAmount, + timestamp: timestamp / 1000, + isoDate: tx.createdAt, + usdValue: -1, + rawTx + } + return standardTx +} From 997c870a84b53037c81638eb0c172c6c4e816a55 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:26:08 -0800 Subject: [PATCH 23/35] Factor out `processPaybisTx` function --- .vscode/settings.json | 3 +- src/partners/paybis.ts | 71 ++++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4205ec68..de804693 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "godex", "Kado", "Libertyx", - "Lifi" + "Lifi", + "Paybis" ] } diff --git a/src/partners/paybis.ts b/src/partners/paybis.ts index 7e921b53..f26134b2 100644 --- a/src/partners/paybis.ts +++ b/src/partners/paybis.ts @@ -169,7 +169,7 @@ export async function queryPaybis( let lastCheckedTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (lastCheckedTimestamp < 0) lastCheckedTimestamp = 0 - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let retry = 0 let startTime = lastCheckedTimestamp @@ -209,38 +209,10 @@ export async function queryPaybis( const txs = asTransactions(jsonObj) cursor = txs.meta.nextCursor for (const rawTx of txs.data) { - const tx = asTransaction(rawTx) - const { amounts, createdAt, gateway, hash, id } = tx - const { spentOriginal, receivedOriginal } = amounts - - const { isoDate, timestamp } = smartIsoDateFromTimestamp( - createdAt.getTime() - ) - - const depositAmount = Number(spentOriginal.amount) - const payoutAmount = Number(receivedOriginal.amount) - const depositTxid = gateway === 'crypto_to_fiat' ? hash : undefined - const payoutTxid = gateway === 'fiat_to_crypto' ? hash : undefined - - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: id, - depositTxid, - depositAddress: undefined, - depositCurrency: spentOriginal.currency, - depositAmount, - payoutTxid, - payoutAddress: tx.to.address ?? undefined, - payoutCurrency: receivedOriginal.currency, - payoutAmount, - timestamp, - isoDate, - usdValue: -1, - rawTx - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + const standardTx = processPaybisTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } } if (cursor == null) { @@ -276,7 +248,7 @@ export async function queryPaybis( const out = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -286,3 +258,34 @@ export const paybis: PartnerPlugin = { pluginName: 'Paybis', pluginId: 'paybis' } + +export function processPaybisTx(rawTx: unknown): StandardTx { + const tx = asTransaction(rawTx) + const { amounts, createdAt, gateway, hash, id } = tx + const { spentOriginal, receivedOriginal } = amounts + + const { isoDate, timestamp } = smartIsoDateFromTimestamp(createdAt.getTime()) + + const depositAmount = Number(spentOriginal.amount) + const payoutAmount = Number(receivedOriginal.amount) + const depositTxid = gateway === 'crypto_to_fiat' ? hash : undefined + const payoutTxid = gateway === 'fiat_to_crypto' ? hash : undefined + + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: id, + depositTxid, + depositAddress: undefined, + depositCurrency: spentOriginal.currency, + depositAmount, + payoutTxid, + payoutAddress: tx.to.address ?? undefined, + payoutCurrency: receivedOriginal.currency, + payoutAmount, + timestamp, + isoDate, + usdValue: -1, + rawTx + } + return standardTx +} From 4ea37e22a6945cc2711e7491d6e887e2d4b06f1c Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:27:57 -0800 Subject: [PATCH 24/35] Factor out `processPaytrieTx` function --- .vscode/settings.json | 3 ++- src/partners/paytrie.ts | 45 +++++++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index de804693..3f6d89de 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "Kado", "Libertyx", "Lifi", - "Paybis" + "Paybis", + "Paytrie" ] } diff --git a/src/partners/paytrie.ts b/src/partners/paytrie.ts index b342ff3a..496a43ec 100644 --- a/src/partners/paytrie.ts +++ b/src/partners/paytrie.ts @@ -20,7 +20,7 @@ const asPaytrieTxs = asArray(asUnknown) export async function queryPaytrie( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let startDate = '2020-01-01' const endDate = new Date().toISOString().slice(0, 10) let apiKey, secretToken @@ -58,29 +58,13 @@ export async function queryPaytrie( const orders = asPaytrieTxs(await apiResponse.json()) for (const rawOrder of orders) { - const order = asPaytrieTx(rawOrder) - const ssTx: StandardTx = { - status: 'complete', - orderId: order.inputTXID, - depositTxid: undefined, - depositAddress: order.inputAddress, - depositCurrency: order.inputCurrency, - depositAmount: order.inputAmount, - payoutTxid: undefined, - payoutAddress: order.outputAddress, - payoutCurrency: order.outputCurrency, - payoutAmount: order.outputAmount, - timestamp: new Date(order.timestamp).getTime() / 1000, - isoDate: order.timestamp, - usdValue: -1, - rawTx: rawOrder - } - ssFormatTxs.push(ssTx) + const standardTx = processPaytrieTx(rawOrder) + standardTxs.push(standardTx) } const out: PluginResult = { settings: { lastCheckedDate: endDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -92,3 +76,24 @@ export const paytrie: PartnerPlugin = { pluginName: 'Paytrie', pluginId: 'paytrie' } + +export function processPaytrieTx(rawTx: unknown): StandardTx { + const order = asPaytrieTx(rawTx) + const standardTx: StandardTx = { + status: 'complete', + orderId: order.inputTXID, + depositTxid: undefined, + depositAddress: order.inputAddress, + depositCurrency: order.inputCurrency, + depositAmount: order.inputAmount, + payoutTxid: undefined, + payoutAddress: order.outputAddress, + payoutCurrency: order.outputCurrency, + payoutAmount: order.outputAmount, + timestamp: new Date(order.timestamp).getTime() / 1000, + isoDate: order.timestamp, + usdValue: -1, + rawTx + } + return standardTx +} From e6a0fab024fc0684546e3d0ac647ae0bb4815459 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:29:17 -0800 Subject: [PATCH 25/35] Factor out `processSafelloTx` function --- .vscode/settings.json | 3 ++- src/partners/safello.ts | 52 +++++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3f6d89de..c88ab915 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "Libertyx", "Lifi", "Paybis", - "Paytrie" + "Paytrie", + "Safello" ] } diff --git a/src/partners/safello.ts b/src/partners/safello.ts index 709ad5a5..3211d619 100644 --- a/src/partners/safello.ts +++ b/src/partners/safello.ts @@ -18,7 +18,7 @@ const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 5 // 5 days export async function querySafello( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let latestTimestamp = 0 if (typeof pluginParams.settings.lastCheckedTimestamp === 'number') { latestTimestamp = pluginParams.settings.lastCheckedTimestamp @@ -45,27 +45,10 @@ export async function querySafello( }) const txs = asSafelloResult(await result.json()) - for (const rawtx of txs.orders) { - const tx = asSafelloTx(rawtx) - const date = new Date(tx.completedDate) - const timestamp = date.getTime() - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: tx.currency, - depositAmount: tx.amount, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: tx.cryptoCurrency, - payoutAmount: 0, - timestamp: timestamp / 1000, - isoDate: date.toISOString(), - usdValue: -1, - rawTx: rawtx - } - ssFormatTxs.push(ssTx) + for (const rawTx of txs.orders) { + const standardTx = processSafelloTx(rawTx) + standardTxs.push(standardTx) + const timestamp = standardTx.timestamp * 1000 if (timestamp > newestTimestamp) { newestTimestamp = timestamp } @@ -84,7 +67,7 @@ export async function querySafello( const out: PluginResult = { settings: { lastCheckedTimestamp: newestTimestamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -96,3 +79,26 @@ export const safello: PartnerPlugin = { pluginName: 'Safello', pluginId: 'safello' } + +export function processSafelloTx(rawTx: unknown): StandardTx { + const tx = asSafelloTx(rawTx) + const date = new Date(tx.completedDate) + const timestamp = date.getTime() + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: tx.currency, + depositAmount: tx.amount, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: tx.cryptoCurrency, + payoutAmount: 0, + timestamp: timestamp / 1000, + isoDate: date.toISOString(), + usdValue: -1, + rawTx + } + return standardTx +} From b68d2278eb44ccd8ef778b8ab6ca8dedc00a44c6 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:30:18 -0800 Subject: [PATCH 26/35] Factor out `processShapeshiftTx` function --- src/partners/shapeshift.ts | 45 +++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/partners/shapeshift.ts b/src/partners/shapeshift.ts index 62e5470f..6530b28c 100644 --- a/src/partners/shapeshift.ts +++ b/src/partners/shapeshift.ts @@ -25,7 +25,7 @@ const asShapeshiftResult = asArray(asUnknown) export async function queryShapeshift( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let apiKey if (typeof pluginParams.apiKeys.apiKey === 'string') { @@ -52,24 +52,8 @@ export async function queryShapeshift( const txs = asShapeshiftResult(result) for (const rawTx of txs) { if (asRawShapeshiftTx(rawTx).status === 'complete') { - const tx = asShapeshiftTx(rawTx) - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.orderId, - depositTxid: tx.inputTXID, - depositAddress: tx.inputAddress, - depositCurrency: tx.inputCurrency, - depositAmount: tx.inputAmount, - payoutTxid: tx.outputTXID, - payoutAddress: tx.outputAddress, - payoutCurrency: tx.outputCurrency, - payoutAmount: safeParseFloat(tx.outputAmount), - timestamp: tx.timestamp, - isoDate: new Date(tx.timestamp * 1000).toISOString(), - usdValue: -1, - rawTx - } - ssFormatTxs.push(ssTx) + const standardTx = processShapeshiftTx(rawTx) + standardTxs.push(standardTx) } } // if (txs.length < 500) { @@ -86,7 +70,7 @@ export async function queryShapeshift( // } const out: PluginResult = { settings: {}, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -98,3 +82,24 @@ export const shapeshift: PartnerPlugin = { pluginName: 'Shapeshift', pluginId: 'shapeshift' } + +export function processShapeshiftTx(rawTx: unknown): StandardTx { + const tx = asShapeshiftTx(rawTx) + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.orderId, + depositTxid: tx.inputTXID, + depositAddress: tx.inputAddress, + depositCurrency: tx.inputCurrency, + depositAmount: tx.inputAmount, + payoutTxid: tx.outputTXID, + payoutAddress: tx.outputAddress, + payoutCurrency: tx.outputCurrency, + payoutAmount: safeParseFloat(tx.outputAmount), + timestamp: tx.timestamp, + isoDate: new Date(tx.timestamp * 1000).toISOString(), + usdValue: -1, + rawTx + } + return standardTx +} From e87e4c098625bd4a9716dc909184ccc25dfeee81 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:31:28 -0800 Subject: [PATCH 27/35] Factor out `processSideshiftTx` function --- .vscode/settings.json | 3 +- src/partners/sideshift.ts | 65 +++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c88ab915..602c62e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,6 +10,7 @@ "Lifi", "Paybis", "Paytrie", - "Safello" + "Safello", + "Sideshift" ] } diff --git a/src/partners/sideshift.ts b/src/partners/sideshift.ts index cbe99725..08113e6c 100644 --- a/src/partners/sideshift.ts +++ b/src/partners/sideshift.ts @@ -104,7 +104,7 @@ export async function querySideshift( let lastCheckedTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (lastCheckedTimestamp < 0) lastCheckedTimestamp = 0 - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let retry = 0 let startTime = lastCheckedTimestamp @@ -130,37 +130,11 @@ export async function querySideshift( if (orders.length === 0) { break } - for (const order of orders) { - let tx: SideshiftTx - try { - tx = asSideshiftTx(order) - } catch (e) { - datelog(e) - throw e - } - const depositAddress = - tx.depositAddress?.address ?? tx.prevDepositAddresses?.address - const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.createdAt) - - const ssTx: StandardTx = { - status: statusMap[tx.status], - orderId: tx.id, - depositTxid: undefined, - depositAddress, - depositCurrency: tx.depositAsset, - depositAmount: Number(tx.invoiceAmount), - payoutTxid: undefined, - payoutAddress: tx.settleAddress.address, - payoutCurrency: tx.settleAsset, - payoutAmount: Number(tx.settleAmount), - timestamp, - isoDate, - usdValue: -1, - rawTx: order - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + for (const rawTx of orders) { + const standardTx = processSideshiftTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } } startTime = new Date(latestIsoDate).getTime() @@ -185,7 +159,7 @@ export async function querySideshift( const out = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -195,3 +169,28 @@ export const sideshift: PartnerPlugin = { pluginName: 'SideShift.ai', pluginId: 'sideshift' } + +export function processSideshiftTx(rawTx: unknown): StandardTx { + const tx: SideshiftTx = asSideshiftTx(rawTx) + const depositAddress = + tx.depositAddress?.address ?? tx.prevDepositAddresses?.address + const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.createdAt) + + const standardTx: StandardTx = { + status: statusMap[tx.status], + orderId: tx.id, + depositTxid: undefined, + depositAddress, + depositCurrency: tx.depositAsset, + depositAmount: Number(tx.invoiceAmount), + payoutTxid: undefined, + payoutAddress: tx.settleAddress.address, + payoutCurrency: tx.settleAsset, + payoutAmount: Number(tx.settleAmount), + timestamp, + isoDate, + usdValue: -1, + rawTx + } + return standardTx +} From 6874a28e6a6c3af6076528bb8d7703737b9a379f Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:33:54 -0800 Subject: [PATCH 28/35] Factor out `processSimplexTx` function --- src/partners/simplex.ts | 57 ++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/partners/simplex.ts b/src/partners/simplex.ts index 8175613b..9c085125 100644 --- a/src/partners/simplex.ts +++ b/src/partners/simplex.ts @@ -23,6 +23,10 @@ const asSimplexTx = asObject({ currency: asString }) +const asPreSimpleTx = asObject({ + created_at: asNumber +}) + const asRawSimplexTx = asObject({ status_name: asString }) @@ -42,7 +46,7 @@ const LIMIT = 100 export async function querySimplex( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let apiKey let lastTimestamp = API_START_DATE if (typeof pluginParams.settings.lastTimestamp === 'number') { @@ -100,31 +104,15 @@ export async function querySimplex( const txs = csvData.data.data for (const rawTx of txs) { if (asRawSimplexTx(rawTx).status_name === 'approved') { - const tx = asSimplexTx(rawTx) - const timestamp = tx.created_at - if (lastTimestamp > timestamp) { + const preTx = asPreSimpleTx(rawTx) + if (lastTimestamp > preTx.created_at) { done = true break } - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.order_id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: tx.currency, - depositAmount: safeParseFloat(tx.fiat_total_amount), - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: tx.crypto_currency, - payoutAmount: safeParseFloat(tx.amount_crypto), - timestamp, - isoDate: new Date(timestamp * 1000).toISOString(), - usdValue: safeParseFloat(tx.amount_usd), - rawTx - } - ssFormatTxs.push(ssTx) - if (timestamp > newestTimestamp) { - newestTimestamp = timestamp + const standardTx = processSimplexTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.timestamp > newestTimestamp) { + newestTimestamp = standardTx.timestamp } } } @@ -137,7 +125,7 @@ export async function querySimplex( const out: PluginResult = { settings: { lastTimestamp: newestTimestamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -149,3 +137,24 @@ export const simplex: PartnerPlugin = { pluginName: 'Simplex', pluginId: 'simplex' } + +export function processSimplexTx(rawTx: unknown): StandardTx { + const tx = asSimplexTx(rawTx) + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.order_id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: tx.currency, + depositAmount: safeParseFloat(tx.fiat_total_amount), + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: tx.crypto_currency, + payoutAmount: safeParseFloat(tx.amount_crypto), + timestamp: tx.created_at, + isoDate: new Date(tx.created_at * 1000).toISOString(), + usdValue: safeParseFloat(tx.amount_usd), + rawTx + } + return standardTx +} From b34df04084dd8e2e69e6f51c9bf0604e903138e2 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:35:57 -0800 Subject: [PATCH 29/35] Factor out `processSwapuzTx` function --- .vscode/settings.json | 3 +- src/partners/swapuz.ts | 88 +++++++++++++++++++----------------------- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 602c62e2..a1d70cc1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,7 @@ "Paybis", "Paytrie", "Safello", - "Sideshift" + "Sideshift", + "Swapuz" ] } diff --git a/src/partners/swapuz.ts b/src/partners/swapuz.ts index 8dc7c0e4..710ebcc5 100644 --- a/src/partners/swapuz.ts +++ b/src/partners/swapuz.ts @@ -61,7 +61,7 @@ const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 5 // 5 days export const querySwapuz = async ( pluginParams: PluginParams ): Promise => { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] const { settings, apiKeys } = asSwapuzPluginParams(pluginParams) const { login, password } = apiKeys @@ -117,55 +117,17 @@ export const querySwapuz = async ( const jsonObj = asSwapuzResult(reply) const { currentPage, maxPage, result: txs } = jsonObj.result for (const rawTx of txs) { - const { - uid, - status: statusNum, - dTxId, - wTxId, - withdrawalTransactionID, - depositTransactionID, - depositAddress, - amount, - amountResult, - createDate, - from, - to - } = asSwapuzTx(rawTx) - - // Status === 6 seems to be the "complete" status - let status: Status = 'other' - if (statusNum === 6) { - status = 'complete' + const standardTx = processSwapuzTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } - - const { isoDate, timestamp } = smartIsoDateFromTimestamp(createDate) - - const ssTx: StandardTx = { - status, - orderId: uid, - depositTxid: dTxId ?? depositTransactionID, - depositCurrency: from.toUpperCase(), - depositAddress, - depositAmount: amount, - payoutTxid: wTxId ?? withdrawalTransactionID, - payoutCurrency: to.toUpperCase(), - payoutAddress: undefined, - payoutAmount: amountResult, - timestamp, - isoDate, - usdValue: -1, - rawTx - } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate - } - if (ssTx.isoDate < oldestIsoDate) { - oldestIsoDate = ssTx.isoDate + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - if (ssTx.isoDate < previousLatestIsoDate && !done) { + if (standardTx.isoDate < previousLatestIsoDate && !done) { datelog( - `Swapuz done: date ${ssTx.isoDate} < ${previousLatestIsoDate}` + `Swapuz done: date ${standardTx.isoDate} < ${previousLatestIsoDate}` ) done = true } @@ -183,7 +145,7 @@ export const querySwapuz = async ( } const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -195,3 +157,33 @@ export const swapuz: PartnerPlugin = { pluginName: 'Swapuz', pluginId: 'swapuz' } + +export function processSwapuzTx(rawTx: unknown): StandardTx { + const tx = asSwapuzTx(rawTx) + + // Status === 6 seems to be the "complete" status + let status: Status = 'other' + if (tx.status === 6) { + status = 'complete' + } + + const { isoDate, timestamp } = smartIsoDateFromTimestamp(tx.createDate) + + const standardTx: StandardTx = { + status, + orderId: tx.uid, + depositTxid: tx.dTxId ?? tx.depositTransactionID, + depositCurrency: tx.from.toUpperCase(), + depositAddress: tx.depositAddress, + depositAmount: tx.amount, + payoutTxid: tx.wTxId ?? tx.withdrawalTransactionID, + payoutCurrency: tx.to.toUpperCase(), + payoutAddress: undefined, + payoutAmount: tx.amountResult, + timestamp, + isoDate, + usdValue: -1, + rawTx + } + return standardTx +} From d215f69692447b25a2d308e54e547535baea590c Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:39:31 -0800 Subject: [PATCH 30/35] Factor out `processSwitchainTx` function --- .vscode/settings.json | 3 +- src/partners/switchain.ts | 62 +++++++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a1d70cc1..941aed1d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "Paytrie", "Safello", "Sideshift", - "Swapuz" + "Swapuz", + "Switchain" ] } diff --git a/src/partners/switchain.ts b/src/partners/switchain.ts index 5e6ff78d..30d5d5d1 100644 --- a/src/partners/switchain.ts +++ b/src/partners/switchain.ts @@ -6,8 +6,6 @@ import { datelog, safeParseFloat } from '../util' const asSwitchainTx = asObject({ id: asString, - status: asString, - appId: asString, createdAt: asString, pair: asString, depositTxId: asString, @@ -18,6 +16,11 @@ const asSwitchainTx = asObject({ rate: asString }) +const asPreSwitchainTx = asObject({ + status: asString, + appId: asString +}) + const asSwitchainResult = asObject({ orders: asArray(asUnknown) }) @@ -28,7 +31,7 @@ const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 4 // 4 days ago export async function querySwitchain( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let apiKey let latestTimestamp = 0 if (typeof pluginParams.settings.latestTimestamp === 'number') { @@ -67,29 +70,13 @@ export async function querySwitchain( } const txs = result.orders - for (const rawtx of txs) { - const tx = asSwitchainTx(rawtx) - if (tx.status === 'confirmed' && tx.appId === apiKey) { - const timestamp = new Date(tx.createdAt).getTime() - const pair = tx.pair.split('-') - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: tx.depositTxId, - depositAddress: tx.depositAddress, - depositCurrency: pair[0].toUpperCase(), - depositAmount: safeParseFloat(tx.amountFrom), - payoutTxid: tx.withdrawTxId, - payoutAddress: tx.withdrawAddress, - payoutCurrency: pair[1].toUpperCase(), - payoutAmount: safeParseFloat(tx.rate), - timestamp: timestamp / 1000, - isoDate: tx.createdAt, - usdValue: -1, - rawTx: rawtx - } + for (const rawTx of txs) { + const preTx = asPreSwitchainTx(rawTx) + if (preTx.status === 'confirmed' && preTx.appId === apiKey) { + const standardTx = processSwitchainTx(rawTx) - ssFormatTxs.push(ssTx) + standardTxs.push(standardTx) + const timestamp = standardTx.timestamp * 1000 if (latestTimestamp - QUERY_LOOKBACK > timestamp) { done = true } @@ -108,7 +95,7 @@ export async function querySwitchain( const out: PluginResult = { settings: { latestTimestamp: newestTimestamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -120,3 +107,26 @@ export const switchain: PartnerPlugin = { pluginName: 'Switchain', pluginId: 'switchain' } + +export function processSwitchainTx(rawTx: unknown): StandardTx { + const tx = asSwitchainTx(rawTx) + const timestamp = new Date(tx.createdAt).getTime() + const pair = tx.pair.split('-') + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.id, + depositTxid: tx.depositTxId, + depositAddress: tx.depositAddress, + depositCurrency: pair[0].toUpperCase(), + depositAmount: safeParseFloat(tx.amountFrom), + payoutTxid: tx.withdrawTxId, + payoutAddress: tx.withdrawAddress, + payoutCurrency: pair[1].toUpperCase(), + payoutAmount: safeParseFloat(tx.rate), + timestamp: timestamp / 1000, + isoDate: tx.createdAt, + usdValue: -1, + rawTx + } + return standardTx +} From 7f57ddf69351e740a1d2b8d141abb652669c5c23 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:51:25 -0800 Subject: [PATCH 31/35] Factor out `processThorchainTx` function --- src/partners/thorchain.ts | 135 +++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 69 deletions(-) diff --git a/src/partners/thorchain.ts b/src/partners/thorchain.ts index 6cc46fe1..fd8b7b06 100644 --- a/src/partners/thorchain.ts +++ b/src/partners/thorchain.ts @@ -68,7 +68,7 @@ const THORCHAIN_MULTIPLIER = 100000000 export const queryThorchain = async ( pluginParams: PluginParams ): Promise => { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] const { settings, apiKeys } = asThorchainPluginParams(pluginParams) const { thorchainAddress } = apiKeys @@ -98,24 +98,17 @@ export const queryThorchain = async ( } const txs = jsonObj.actions for (const rawTx of txs) { - const { - date, - in: txIns, - out: txOuts, - pools, - status: txStatus - } = asThorchainTx(rawTx) // Check RAW trasaction - - const status = 'complete' - if (txStatus !== 'success') { + const tx = asThorchainTx(rawTx) + + if (tx.status !== 'success') { continue } - if (pools.length !== 2) { + if (tx.pools.length !== 2) { continue } - const srcAsset = txIns[0].coins[0].asset - const match = txOuts.find(o => { + const srcAsset = tx.in[0].coins[0].asset + const match = tx.out.find(o => { const match2 = o.coins.find(c => c.asset === srcAsset) return match2 != null }) @@ -126,74 +119,37 @@ export const queryThorchain = async ( continue } - const timestampMs = div(date, '1000000', 16) - const { timestamp, isoDate } = smartIsoDateFromTimestamp( - Number(timestampMs) - ) - - const [chainAsset] = srcAsset.split('-') - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [_chain, asset] = chainAsset.split('.') - - const depositAmount = - Number(txIns[0].coins[0].amount) / THORCHAIN_MULTIPLIER - - const txOut = txOuts.find(o => o.coins[0].asset !== 'THOR.RUNE') + const txOut = tx.out.find(o => o.coins[0].asset !== 'THOR.RUNE') if (txOut == null) { continue } - let payoutCurrency - let payoutAmount = 0 - if (txOut != null) { - const [destChainAsset] = txOut.coins[0].asset.split('-') - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [_destChain, destAsset] = destChainAsset.split('.') - payoutCurrency = destAsset - payoutAmount = Number(txOut.coins[0].amount) / THORCHAIN_MULTIPLIER - } - - const ssTx: StandardTx = { - status, - orderId: txIns[0].txID, - depositTxid: txIns[0].txID, - depositAddress: undefined, - depositCurrency: asset.toUpperCase(), - depositAmount, - payoutTxid: txOut?.txID, - payoutAddress: txOut?.address, - payoutCurrency, - payoutAmount, - timestamp, - isoDate, - usdValue: -1, - rawTx - } + const standardTx = processThorchainTx(rawTx) // See if the transaction exists already - const matchTx = ssFormatTxs.find( + const matchTx = standardTxs.find( tx => - tx.orderId === ssTx.orderId && - tx.timestamp === ssTx.timestamp && - tx.depositCurrency === ssTx.depositCurrency && - tx.payoutCurrency === ssTx.payoutCurrency && - tx.payoutAmount === ssTx.payoutAmount && - tx.depositAmount !== ssTx.depositAmount + tx.orderId === standardTx.orderId && + tx.timestamp === standardTx.timestamp && + tx.depositCurrency === standardTx.depositCurrency && + tx.payoutCurrency === standardTx.payoutCurrency && + tx.payoutAmount === standardTx.payoutAmount && + tx.depositAmount !== standardTx.depositAmount ) if (matchTx == null) { - ssFormatTxs.push(ssTx) + standardTxs.push(standardTx) } else { - matchTx.depositAmount += ssTx.depositAmount + matchTx.depositAmount += standardTx.depositAmount } - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } - if (ssTx.isoDate < oldestIsoDate) { - oldestIsoDate = ssTx.isoDate + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - if (ssTx.isoDate < previousLatestIsoDate && !done) { + if (standardTx.isoDate < previousLatestIsoDate && !done) { datelog( - `Thorchain done: date ${ssTx.isoDate} < ${previousLatestIsoDate}` + `Thorchain done: date ${standardTx.isoDate} < ${previousLatestIsoDate}` ) done = true } @@ -206,7 +162,7 @@ export const queryThorchain = async ( } const out: PluginResult = { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -218,3 +174,44 @@ export const thorchain: PartnerPlugin = { pluginName: 'Thorchain', pluginId: 'thorchain' } + +export function processThorchainTx(rawTx: unknown): StandardTx { + const tx = asThorchainTx(rawTx) + const srcAsset = tx.in[0].coins[0].asset + const [chainAsset] = srcAsset.split('-') + const [, asset] = chainAsset.split('.') + + const depositAmount = Number(tx.in[0].coins[0].amount) / THORCHAIN_MULTIPLIER + + const txOut = tx.out.find(o => o.coins[0].asset !== 'THOR.RUNE') + + if (txOut == null) { + throw new Error('Unable to find THOR.RUNE asset in tx.out') + } + + const [destChainAsset] = txOut.coins[0].asset.split('-') + const [, destAsset] = destChainAsset.split('.') + const payoutCurrency = destAsset + const payoutAmount = Number(txOut.coins[0].amount) / THORCHAIN_MULTIPLIER + + const timestampMs = div(tx.date, '1000000', 16) + const { timestamp, isoDate } = smartIsoDateFromTimestamp(Number(timestampMs)) + + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.in[0].txID, + depositTxid: tx.in[0].txID, + depositAddress: undefined, + depositCurrency: asset.toUpperCase(), + depositAmount, + payoutTxid: txOut?.txID, + payoutAddress: txOut?.address, + payoutCurrency, + payoutAmount, + timestamp, + isoDate, + usdValue: -1, + rawTx + } + return standardTx +} From 8dc26636402e47d15aaa42401179fbf31a4198c0 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 16:58:49 -0800 Subject: [PATCH 32/35] Factor out `processTransakTx` function --- .vscode/settings.json | 3 +- src/partners/transak.ts | 66 +++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 941aed1d..718af83e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,7 @@ "Safello", "Sideshift", "Swapuz", - "Switchain" + "Switchain", + "Transak" ] } diff --git a/src/partners/transak.ts b/src/partners/transak.ts index d3eef01b..e620ca9c 100644 --- a/src/partners/transak.ts +++ b/src/partners/transak.ts @@ -30,7 +30,7 @@ const asTransakOrder = asObject({ type TransakOrder = ReturnType -const asRawTxOrder = asObject({ +const asPreTransakOrder = asObject({ status: asString }) @@ -41,7 +41,7 @@ const asTransakResult = asObject({ export async function queryTransak( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let apiKey: string let { offset = 0 } = pluginParams.settings @@ -68,38 +68,10 @@ export async function queryTransak( } const txs = resultJSON.response - for (const rawtx of txs) { - if (asRawTxOrder(rawtx).status === 'COMPLETED') { - let tx: TransakOrder - try { - tx = asTransakOrder(rawtx) - } catch (e) { - datelog(e) - datelog(rawtx) - throw e - } - const date = new Date(tx.completedAt) - const depositAddress = - typeof tx.fromWalletAddress === 'string' - ? tx.fromWalletAddress - : undefined - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: undefined, - depositAddress, - depositCurrency: tx.fiatCurrency, - depositAmount: tx.fiatAmount, - payoutTxid: undefined, - payoutAddress: tx.walletAddress, - payoutCurrency: tx.cryptoCurrency, - payoutAmount: tx.cryptoAmount, - timestamp: date.getTime() / 1000, - isoDate: date.toISOString(), - usdValue: -1, - rawTx: rawtx - } - ssFormatTxs.push(ssTx) + for (const rawTx of txs) { + if (asPreTransakOrder(rawTx).status === 'COMPLETED') { + const standardTx = processTransakTx(rawTx) + standardTxs.push(standardTx) } } if (txs.length < PAGE_LIMIT) { @@ -112,7 +84,7 @@ export async function queryTransak( const out: PluginResult = { settings: { offset }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -124,3 +96,27 @@ export const transak: PartnerPlugin = { pluginName: 'Transak', pluginId: 'transak' } + +export function processTransakTx(rawTx: unknown): StandardTx { + const tx: TransakOrder = asTransakOrder(rawTx) + const date = new Date(tx.completedAt) + const depositAddress = + typeof tx.fromWalletAddress === 'string' ? tx.fromWalletAddress : undefined + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.id, + depositTxid: undefined, + depositAddress, + depositCurrency: tx.fiatCurrency, + depositAmount: tx.fiatAmount, + payoutTxid: undefined, + payoutAddress: tx.walletAddress, + payoutCurrency: tx.cryptoCurrency, + payoutAmount: tx.cryptoAmount, + timestamp: date.getTime() / 1000, + isoDate: date.toISOString(), + usdValue: -1, + rawTx + } + return standardTx +} From aebf68e25efd077bad997d3f09869924a4f7ca9d Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 17:08:10 -0800 Subject: [PATCH 33/35] Factor out `processWyreTx` function --- .vscode/settings.json | 3 +- src/partners/wyre.ts | 85 ++++++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 718af83e..75e0b1ef 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,6 +14,7 @@ "Sideshift", "Swapuz", "Switchain", - "Transak" + "Transak", + "Wyre" ] } diff --git a/src/partners/wyre.ts b/src/partners/wyre.ts index 3277f38c..417bbfa8 100644 --- a/src/partners/wyre.ts +++ b/src/partners/wyre.ts @@ -34,7 +34,7 @@ const asWyreParams = asObject({ export async function queryWyre( pluginParams: PluginParams ): Promise { - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] // lastTxTimestamp is in JS milliseconds let apiKey, lastTxTimestamp @@ -53,23 +53,6 @@ export async function queryWyre( let newestTxTimestamp = lastTxTimestamp - QUERY_LOOKBACK if (newestTxTimestamp < 0) newestTxTimestamp = 0 - const parseTxStr = (txStr): WyreTx => { - const txItems = txStr.split(',') - return { - id: txItems[0], - owner: txItems[1], - status: txItems[2], - createdAt: txItems[3], - completedAt: txItems[4], - sourceAmount: txItems[5], - sourceCurrency: txItems[6], - usdFeeEquiv: txItems[7], - destAmount: txItems[8], - destCurrency: txItems[9], - usdEquiv: txItems[10] - } - } - const url = `https://app.periscopedata.com/api/sendwyre/chart/csv/${apiKey}` const result = await fetch(url, { method: 'GET' @@ -79,8 +62,8 @@ export async function queryWyre( txs.shift() txs.pop() - for (const txStr of txs) { - const tx = asWyreTx(parseTxStr(txStr)) + for (const rawTx of txs) { + const tx = asWyreTx(parseTxStr(rawTx)) if (tx.status === 'COMPLETED' && tx.sourceCurrency !== tx.destCurrency) { const date = new Date(tx.createdAt) const dateMs = date.getTime() @@ -91,30 +74,15 @@ export async function queryWyre( newestTxTimestamp = dateMs } - const ssTx: StandardTx = { - status: 'complete', - orderId: tx.id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: tx.sourceCurrency, - depositAmount: safeParseFloat(tx.sourceAmount), - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: tx.destCurrency, - payoutAmount: safeParseFloat(tx.destAmount), - timestamp: dateMs / 1000, - isoDate: date.toISOString(), - usdValue: safeParseFloat(tx.usdEquiv), - rawTx: txStr - } + const standardTx = processWyreTx(rawTx) - ssFormatTxs.push(ssTx) + standardTxs.push(standardTx) } } const out: PluginResult = { settings: { lastTxTimestamp: newestTxTimestamp }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -126,3 +94,44 @@ export const wyre: PartnerPlugin = { pluginName: 'Wyre', pluginId: 'wyre' } + +export function processWyreTx(rawTx: unknown): StandardTx { + const tx = asWyreTx(parseTxStr(asString(rawTx))) + const date = new Date(tx.createdAt) + const dateMs = date.getTime() + + const standardTx: StandardTx = { + status: 'complete', + orderId: tx.id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: tx.sourceCurrency, + depositAmount: safeParseFloat(tx.sourceAmount), + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: tx.destCurrency, + payoutAmount: safeParseFloat(tx.destAmount), + timestamp: dateMs / 1000, + isoDate: date.toISOString(), + usdValue: safeParseFloat(tx.usdEquiv), + rawTx + } + return standardTx +} + +const parseTxStr = (txStr: string): WyreTx => { + const txItems = txStr.split(',') + return { + id: txItems[0], + owner: txItems[1], + status: txItems[2], + createdAt: txItems[3], + completedAt: txItems[4], + sourceAmount: txItems[5], + sourceCurrency: txItems[6], + usdFeeEquiv: txItems[7], + destAmount: txItems[8], + destCurrency: txItems[9], + usdEquiv: txItems[10] + } +} From a635086362538b8af353b8276046f32750e5e8e4 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 8 Jan 2025 17:10:31 -0800 Subject: [PATCH 34/35] Factor out `processXanpoolTx` function --- .vscode/settings.json | 3 +- src/partners/xanpool.ts | 115 ++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 63 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 75e0b1ef..8359d013 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,6 +15,7 @@ "Swapuz", "Switchain", "Transak", - "Wyre" + "Wyre", + "Xanpool" ] } diff --git a/src/partners/xanpool.ts b/src/partners/xanpool.ts index 7864c7db..9fd569ed 100644 --- a/src/partners/xanpool.ts +++ b/src/partners/xanpool.ts @@ -59,7 +59,7 @@ async function queryXanpool(pluginParams: PluginParams): Promise { return { settings: { latestIsoDate }, transactions: [] } } - const ssFormatTxs: StandardTx[] = [] + const standardTxs: StandardTx[] = [] let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK if (previousTimestamp < 0) previousTimestamp = 0 const previousLatestIsoDate = new Date(previousTimestamp).toISOString() @@ -81,69 +81,17 @@ async function queryXanpool(pluginParams: PluginParams): Promise { break } for (const rawTx of txs) { - const { - id, - status, - type, - blockchainTxId, - wallet, - createdAt, - currency, - fiat, - cryptoCurrency, - crypto, - depositWallets - } = asXanpoolTx(rawTx) - let ssTx: StandardTx - if (type === 'buy') { - ssTx = { - status: status === 'completed' ? 'complete' : 'expired', - orderId: id, - depositTxid: undefined, - depositAddress: undefined, - depositCurrency: currency, - depositAmount: fiat, - payoutTxid: blockchainTxId, - payoutAddress: wallet, - payoutCurrency: cryptoCurrency, - payoutAmount: crypto, - timestamp: smartIsoDateFromTimestamp(new Date(createdAt).getTime()) - .timestamp, - isoDate: createdAt, - usdValue: -1, - rawTx - } - } else if (type === 'sell') { - ssTx = { - status: status === 'completed' ? 'complete' : 'expired', - orderId: id, - depositTxid: blockchainTxId, - depositAddress: Object.values(depositWallets ?? {})[0], - depositCurrency: cryptoCurrency, - depositAmount: crypto, - payoutTxid: undefined, - payoutAddress: undefined, - payoutCurrency: currency, - payoutAmount: fiat, - timestamp: smartIsoDateFromTimestamp(new Date(createdAt).getTime()) - .timestamp, - isoDate: createdAt, - usdValue: -1, - rawTx - } - } else { - throw new Error(`Invalid tx type ${type}`) + const standardTx = processXanpoolTx(rawTx) + standardTxs.push(standardTx) + if (standardTx.isoDate > latestIsoDate) { + latestIsoDate = standardTx.isoDate } - ssFormatTxs.push(ssTx) - if (ssTx.isoDate > latestIsoDate) { - latestIsoDate = ssTx.isoDate + if (standardTx.isoDate < oldestIsoDate) { + oldestIsoDate = standardTx.isoDate } - if (ssTx.isoDate < oldestIsoDate) { - oldestIsoDate = ssTx.isoDate - } - if (ssTx.isoDate < previousLatestIsoDate && !done) { + if (standardTx.isoDate < previousLatestIsoDate && !done) { datelog( - `Xanpool done: date ${ssTx.isoDate} < ${previousLatestIsoDate}` + `Xanpool done: date ${standardTx.isoDate} < ${previousLatestIsoDate}` ) done = true } @@ -158,7 +106,7 @@ async function queryXanpool(pluginParams: PluginParams): Promise { settings: { latestIsoDate }, - transactions: ssFormatTxs + transactions: standardTxs } return out } @@ -170,3 +118,46 @@ export const xanpool: PartnerPlugin = { pluginName: 'Xanpool', pluginId: 'xanpool' } + +export function processXanpoolTx(rawTx: unknown): StandardTx { + const tx = asXanpoolTx(rawTx) + if (tx.type === 'buy') { + return { + status: tx.status === 'completed' ? 'complete' : 'expired', + orderId: tx.id, + depositTxid: undefined, + depositAddress: undefined, + depositCurrency: tx.currency, + depositAmount: tx.fiat, + payoutTxid: tx.blockchainTxId, + payoutAddress: tx.wallet, + payoutCurrency: tx.cryptoCurrency, + payoutAmount: tx.crypto, + timestamp: smartIsoDateFromTimestamp(new Date(tx.createdAt).getTime()) + .timestamp, + isoDate: tx.createdAt, + usdValue: -1, + rawTx + } + } else if (tx.type === 'sell') { + return { + status: tx.status === 'completed' ? 'complete' : 'expired', + orderId: tx.id, + depositTxid: tx.blockchainTxId, + depositAddress: Object.values(tx.depositWallets ?? {})[0], + depositCurrency: tx.cryptoCurrency, + depositAmount: tx.crypto, + payoutTxid: undefined, + payoutAddress: undefined, + payoutCurrency: tx.currency, + payoutAmount: tx.fiat, + timestamp: smartIsoDateFromTimestamp(new Date(tx.createdAt).getTime()) + .timestamp, + isoDate: tx.createdAt, + usdValue: -1, + rawTx + } + } else { + throw new Error(`Invalid tx type ${tx.type}`) + } +} From 9e41364de393b506067f120f6fc807e57506f6d5 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 9 Jan 2025 18:30:24 -0800 Subject: [PATCH 35/35] Add direction and exchangeType to StandardTx --- .vscode/settings.json | 1 + src/bin/migration.ts | 14 +++++- src/partners/banxa.ts | 53 +++++++++++++++++++-- src/partners/bitaccess.ts | 19 +++++++- src/partners/bitrefill.ts | 5 ++ src/partners/bitsofgold.ts | 11 +++++ src/partners/bity.ts | 16 +++++++ src/partners/changehero.ts | 4 ++ src/partners/changelly.ts | 4 ++ src/partners/changenow.ts | 4 ++ src/partners/coinswitch.ts | 4 ++ src/partners/exolix.ts | 4 ++ src/partners/faast.ts | 4 ++ src/partners/foxExchange.ts | 4 ++ src/partners/godex.ts | 4 ++ src/partners/ioniagiftcard.ts | 4 ++ src/partners/ioniavisarewards.ts | 4 ++ src/partners/kado.ts | 27 +++++++++++ src/partners/letsexchange.ts | 4 ++ src/partners/libertyx.ts | 4 ++ src/partners/lifi.ts | 4 ++ src/partners/moonpay.ts | 74 ++++++++++++++++++++++++---- src/partners/paybis.ts | 82 ++++++++++++++++++++++++++------ src/partners/paytrie.ts | 4 ++ src/partners/safello.ts | 4 ++ src/partners/shapeshift.ts | 4 ++ src/partners/sideshift.ts | 4 ++ src/partners/simplex.ts | 24 +++++++++- src/partners/swapuz.ts | 5 ++ src/partners/switchain.ts | 4 ++ src/partners/thorchain.ts | 4 ++ src/partners/totle.ts | 4 ++ src/partners/transak.ts | 43 ++++++++++++++++- src/partners/wyre.ts | 22 ++++++++- src/partners/xanpool.ts | 11 ++++- src/types.ts | 50 +++++++++++++++++-- src/util/fiatCurrency.ts | 19 ++++++++ 37 files changed, 516 insertions(+), 40 deletions(-) create mode 100644 src/util/fiatCurrency.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 8359d013..5d28cf45 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "banxa", "Bitrefill", "Bity", "Changelly", diff --git a/src/bin/migration.ts b/src/bin/migration.ts index 93ff6950..e65a4347 100644 --- a/src/bin/migration.ts +++ b/src/bin/migration.ts @@ -151,7 +151,12 @@ async function migration(): Promise { cleanedShapeshiftTx.timestamp * 1000 ).toISOString(), usdValue: -1, - rawTx: tx + rawTx: tx, + // IDK what to do here... + countryCode: null, + direction: null, + exchangeType: 'swap', + paymentType: null } return await standardTxReformat( newTx, @@ -196,7 +201,12 @@ async function migration(): Promise { isoDate, timestamp, usdValue: -1, - rawTx: undefined + rawTx: undefined, + // IDK what to do here... + countryCode: null, + direction: null, + exchangeType: 'swap', + paymentType: null } return await standardTxReformat( newTx, diff --git a/src/partners/banxa.ts b/src/partners/banxa.ts index 5780ab34..790c0a77 100644 --- a/src/partners/banxa.ts +++ b/src/partners/banxa.ts @@ -13,6 +13,7 @@ import { Response } from 'node-fetch' import { EDGE_APP_START_DATE, + FiatPaymentType, PartnerPlugin, PluginParams, PluginResult, @@ -32,6 +33,7 @@ export const asBanxaParams = asObject({ }) }) +type BanxaStatus = ReturnType const asBanxaStatus = asMaybe( asValue( 'complete', @@ -44,15 +46,18 @@ const asBanxaStatus = asMaybe( 'other' ) +type BanxaTx = ReturnType const asBanxaTx = asObject({ id: asString, status: asBanxaStatus, created_at: asString, + country: asString, fiat_amount: asNumber, fiat_code: asString, coin_amount: asNumber, coin_code: asString, order_type: asString, + payment_type: asString, wallet_address: asMaybe(asString, '') }) @@ -67,9 +72,6 @@ const PAGE_LIMIT = 100 const ONE_DAY_MS = 1000 * 60 * 60 * 24 const ROLLBACK = ONE_DAY_MS * 7 // 7 days -type BanxaTx = ReturnType -type BanxaStatus = ReturnType - const statusMap: { [key in BanxaStatus]: Status } = { complete: 'complete', expired: 'expired', @@ -263,13 +265,21 @@ export function processBanxaTx(rawTx: unknown): StandardTx { payoutAddress = banxaTx.wallet_address } + const direction = banxaTx.order_type === 'CRYPTO-SELL' ? 'sell' : 'buy' + + const paymentType = getFiatPaymentType(banxaTx) + const standardTx: StandardTx = { status: statusMap[banxaTx.status], orderId: banxaTx.id, + countryCode: banxaTx.country, depositTxid: undefined, depositAddress: undefined, depositCurrency: inputCurrency, depositAmount: inputAmount, + direction, + exchangeType: 'fiat', + paymentType, payoutTxid: undefined, payoutAddress, payoutCurrency: outputCurrency, @@ -282,3 +292,40 @@ export function processBanxaTx(rawTx: unknown): StandardTx { return standardTx } + +function getFiatPaymentType(tx: BanxaTx): FiatPaymentType { + switch (tx.payment_type) { + case 'AusPost Retail': + case 'BPay': + case 'Blueshyft Online': + case 'POLi Transfer': + case 'Sofort Transfer': + return 'directtobank' + case 'Checkout Credit Card': + case 'WorldPay Credit Card': + return 'credit' + case 'ClearJunction Fast Pay': + case 'ClearJunction Sell Fast Pay': + return 'fasterpayments' + case 'ClearJunction Sepa': + case 'Ten31 Sepa': + return 'sepa' + case 'DCBank Interac': + case 'DCBank Interac Sell': + return 'interac' + case 'Enumis Transfer': + return 'wire' + case 'Monoova Sell': + case 'NPP PayID': + case 'PayID via Monoova': + return 'payid' + case 'WorldPay ApplePay': + return 'applepay' + case 'WorldPay GooglePay': + return 'googlepay' + case 'iDEAL Transfer': + return 'ideal' + default: + throw new Error(`Unknown payment method: ${tx.payment_type} for ${tx.id}`) + } +} diff --git a/src/partners/bitaccess.ts b/src/partners/bitaccess.ts index 56a27bdb..0f98bf85 100644 --- a/src/partners/bitaccess.ts +++ b/src/partners/bitaccess.ts @@ -4,7 +4,8 @@ import { asObject, asOptional, asString, - asUnknown + asUnknown, + asValue } from 'cleaners' import crypto from 'crypto' import fetch from 'node-fetch' @@ -13,11 +14,13 @@ import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' import { datelog } from '../util' const asBitaccessTx = asObject({ + trade_type: asValue<['buy', 'sell']>('buy', 'sell'), transaction_id: asString, tx_hash: asOptional(asString), deposit_address: asString, deposit_currency: asString, deposit_amount: asNumber, + location_address: asString, withdrawal_address: asOptional(asString), withdrawal_currency: asString, withdrawal_amount: asNumber, @@ -132,13 +135,19 @@ export function processBitaccessTx(rawTx: unknown): StandardTx { payoutTxid = tx.tx_hash } + const countryCode = parseLocationAddressForCountryCode(tx.location_address) + const standardTx: StandardTx = { status: 'complete', orderId: tx.transaction_id, + countryCode, depositTxid, depositAddress: tx.deposit_address, depositCurrency: tx.deposit_currency.toUpperCase(), depositAmount: tx.deposit_amount, + direction: tx.trade_type, + exchangeType: 'fiat', + paymentType: null, payoutTxid, payoutAddress: tx.withdrawal_address, payoutCurrency: tx.withdrawal_currency.toUpperCase(), @@ -150,3 +159,11 @@ export function processBitaccessTx(rawTx: unknown): StandardTx { } return standardTx } + +function parseLocationAddressForCountryCode(location: string): string { + const parts = location.split(', ') + if (parts.length !== 4 || parts[3].length > 3) { + throw new Error(`Unexpected location address: ${location}`) + } + return parts[3] +} diff --git a/src/partners/bitrefill.ts b/src/partners/bitrefill.ts index ce759c3a..2552f7a0 100644 --- a/src/partners/bitrefill.ts +++ b/src/partners/bitrefill.ts @@ -21,6 +21,7 @@ const asBitrefillTx = asObject({ satoshiPrice: asOptional(asNumber), value: asString, currency: asString, + country: asString, coinCurrency: asString, receivedPaymentAltcoin: asOptional(asNumber), orderId: asString, @@ -146,10 +147,14 @@ export function processBitrefillTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.orderId, + countryCode: tx.country.toUpperCase(), depositTxid: undefined, depositAddress: undefined, depositCurrency: inputCurrency, depositAmount, + direction: 'sell', + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: tx.currency, diff --git a/src/partners/bitsofgold.ts b/src/partners/bitsofgold.ts index f3a39640..1381c8d4 100644 --- a/src/partners/bitsofgold.ts +++ b/src/partners/bitsofgold.ts @@ -106,13 +106,24 @@ export function processBitsOfGoldTx(rawTx: unknown): StandardTx { payoutAmount = data.fiat_amount } + const direction = + bogTx.type === 'sell' ? 'sell' : bogTx.type === 'buy' ? 'buy' : undefined + + if (direction == null) { + throw new Error(`Invalid direction ${bogTx.type}`) + } + const standardTx: StandardTx = { status: 'complete', orderId: bogTx.id, + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency, depositAmount, + direction, + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, payoutCurrency, diff --git a/src/partners/bity.ts b/src/partners/bity.ts index a040bdbd..b7fe50d3 100644 --- a/src/partners/bity.ts +++ b/src/partners/bity.ts @@ -158,13 +158,29 @@ export const bity: PartnerPlugin = { export function processBityTx(rawTx: unknown): StandardTx { const tx = asBityTx(rawTx) + // Assume that one currency is EUR and that's the only fiat currency supported + // by Bity. + if ( + tx.input.currency.toUpperCase() !== 'EUR' && + tx.output.currency.toUpperCase() !== 'EUR' + ) { + throw new Error( + `Unknown fiat currency ${tx.input.currency} or ${tx.output.currency}` + ) + } + const direction = tx.input.currency.toUpperCase() === 'EUR' ? 'buy' : 'sell' + const standardTx: StandardTx = { status: 'complete', orderId: tx.id, + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency: tx.input.currency.toUpperCase(), depositAmount: safeParseFloat(tx.input.amount), + direction, + exchangeType: 'swap', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: tx.output.currency.toUpperCase(), diff --git a/src/partners/changehero.ts b/src/partners/changehero.ts index 7f333dd1..7ca5ccd9 100644 --- a/src/partners/changehero.ts +++ b/src/partners/changehero.ts @@ -164,10 +164,14 @@ export function processChangeHeroTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap[tx.status], orderId: tx.id, + countryCode: null, depositTxid: tx.payinHash, depositAddress: tx.payinAddress, depositCurrency: tx.currencyFrom.toUpperCase(), depositAmount: safeParseFloat(tx.amountFrom), + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.payoutHash, payoutAddress: tx.payoutAddress, payoutCurrency: tx.currencyTo.toUpperCase(), diff --git a/src/partners/changelly.ts b/src/partners/changelly.ts index 5eeebc50..a961ae2f 100644 --- a/src/partners/changelly.ts +++ b/src/partners/changelly.ts @@ -176,10 +176,14 @@ export function processChangellyTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.id, + countryCode: null, depositTxid: tx.payinHash, depositAddress: tx.payinAddress, depositCurrency: tx.currencyFrom.toUpperCase(), depositAmount: safeParseFloat(tx.amountFrom), + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.payoutHash, payoutAddress: tx.payoutAddress, payoutCurrency: tx.currencyTo.toUpperCase(), diff --git a/src/partners/changenow.ts b/src/partners/changenow.ts index 233e9eb2..29d22107 100644 --- a/src/partners/changenow.ts +++ b/src/partners/changenow.ts @@ -147,10 +147,14 @@ export function processChangeNowTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap[tx.status], orderId: tx.requestId, + countryCode: null, depositTxid: tx.payin.hash, depositAddress: tx.payin.address, depositCurrency: tx.payin.currency.toUpperCase(), depositAmount: tx.payin.amount ?? tx.payin.expectedAmount ?? 0, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.payout.hash, payoutAddress: tx.payout.address, payoutCurrency: tx.payout.currency.toUpperCase(), diff --git a/src/partners/coinswitch.ts b/src/partners/coinswitch.ts index a539ea12..a4c29a3b 100644 --- a/src/partners/coinswitch.ts +++ b/src/partners/coinswitch.ts @@ -112,10 +112,14 @@ export function processCoinSwitchTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.orderId, + countryCode: null, depositTxid, depositAddress: tx.exchangeAddress.address, depositCurrency: tx.depositCoin.toUpperCase(), depositAmount: tx.depositCoinAmount, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid, payoutAddress: tx.destinationAddress.address, payoutCurrency: tx.destinationCoin.toUpperCase(), diff --git a/src/partners/exolix.ts b/src/partners/exolix.ts index 60c839a5..a98a4838 100644 --- a/src/partners/exolix.ts +++ b/src/partners/exolix.ts @@ -171,10 +171,14 @@ export function processExolixTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap[tx.status], orderId: tx.id, + countryCode: null, depositTxid: tx.hashIn?.hash ?? '', depositAddress: tx.depositAddress, depositCurrency: tx.coinFrom.coinCode, depositAmount: tx.amount, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.hashOut?.hash ?? '', payoutAddress: tx.withdrawalAddress, payoutCurrency: tx.coinTo.coinCode, diff --git a/src/partners/faast.ts b/src/partners/faast.ts index 9036129b..d3badcc6 100644 --- a/src/partners/faast.ts +++ b/src/partners/faast.ts @@ -112,10 +112,14 @@ export function processFaastTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.swap_id, + countryCode: null, depositTxid: undefined, depositAddress: tx.deposit_address, depositCurrency: tx.deposit_currency.toUpperCase(), depositAmount: tx.amount_deposited, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.transaction_id, payoutAddress: tx.withdrawal_address, payoutCurrency: tx.withdrawal_currency.toUpperCase(), diff --git a/src/partners/foxExchange.ts b/src/partners/foxExchange.ts index c77ba9c5..98bb453a 100644 --- a/src/partners/foxExchange.ts +++ b/src/partners/foxExchange.ts @@ -128,10 +128,14 @@ export function processFoxExchangeTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.orderId, + countryCode: null, depositTxid: undefined, depositAddress: tx.exchangeAddress.address, depositCurrency: tx.depositCoin.toUpperCase(), depositAmount: tx.depositCoinAmount, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.outputTransactionHash, payoutAddress: tx.destinationAddress.address, payoutCurrency: tx.destinationCoin.toUpperCase(), diff --git a/src/partners/godex.ts b/src/partners/godex.ts index 3b396e9e..c835eb3d 100644 --- a/src/partners/godex.ts +++ b/src/partners/godex.ts @@ -155,10 +155,14 @@ export function processGodexTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap[tx.status], orderId: tx.transaction_id, + countryCode: null, depositTxid: tx.hash_in, depositAddress: tx.deposit, depositCurrency: tx.coin_from.toUpperCase(), depositAmount: safeParseFloat(tx.deposit_amount), + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: undefined, payoutAddress: tx.withdrawal, payoutCurrency: tx.coin_to.toUpperCase(), diff --git a/src/partners/ioniagiftcard.ts b/src/partners/ioniagiftcard.ts index 923c45c2..806c3ba3 100644 --- a/src/partners/ioniagiftcard.ts +++ b/src/partners/ioniagiftcard.ts @@ -141,10 +141,14 @@ export function processIoniaGiftCardsTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap.complete, orderId: tx.Id.toString(), + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency: 'USD', depositAmount: tx.USDPaidByCustomer, + direction: 'sell', + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: 'USD', diff --git a/src/partners/ioniavisarewards.ts b/src/partners/ioniavisarewards.ts index 3dd94ff9..ef222165 100644 --- a/src/partners/ioniavisarewards.ts +++ b/src/partners/ioniavisarewards.ts @@ -141,10 +141,14 @@ export function processIoniaVisaRewardsTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap.complete, orderId: tx.Id.toString(), + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency: 'USD', depositAmount: tx.USDPaidByCustomer, + direction: 'sell', + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: 'USD', diff --git a/src/partners/kado.ts b/src/partners/kado.ts index a4d3a98b..0ffe9881 100644 --- a/src/partners/kado.ts +++ b/src/partners/kado.ts @@ -3,6 +3,7 @@ import { asBoolean, asDate, asEither, + asNull, asNumber, asObject, asString, @@ -11,6 +12,7 @@ import { import { asStandardPluginParams, + FiatPaymentType, PartnerPlugin, PluginParams, PluginResult, @@ -45,6 +47,7 @@ const asOffRampTx = asObject({ // disburseMethod: asString }) +type KadoTx = ReturnType const asKadoTx = asEither(asOnRampTx, asOffRampTx) // Define cleaner for the main data structure @@ -126,10 +129,14 @@ export function processKadoTx(rawTx: unknown): StandardTx { return { status: 'complete', orderId: tx._id, + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency: 'USD', depositAmount: tx.paidAmountUsd, + direction: tx.type, + exchangeType: 'fiat', + paymentType: getFiatPaymentType(tx), payoutTxid: undefined, payoutAddress: tx.walletAddress, payoutCurrency: tx.cryptoCurrency, @@ -143,10 +150,14 @@ export function processKadoTx(rawTx: unknown): StandardTx { return { status: 'complete', orderId: tx._id, + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency: tx.cryptoCurrency, depositAmount: tx.depositUnitCount, + direction: tx.type, + exchangeType: 'fiat', + paymentType: getFiatPaymentType(tx), payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: 'USD', @@ -158,3 +169,19 @@ export function processKadoTx(rawTx: unknown): StandardTx { } } } + +function getFiatPaymentType(tx: KadoTx): FiatPaymentType | null { + if (!('paymentMethod' in tx)) return null + switch (tx.paymentMethod) { + case 'deposit_ach': { + if (tx.type === 'buy') return 'iach' + return 'ach' + } + case 'wire_transfer': + return 'wire' + default: + throw new Error( + `Unknown payment method: ${tx.paymentMethod} for ${tx._id}` + ) + } +} diff --git a/src/partners/letsexchange.ts b/src/partners/letsexchange.ts index bb125836..8cd1e462 100644 --- a/src/partners/letsexchange.ts +++ b/src/partners/letsexchange.ts @@ -165,10 +165,14 @@ export function processLetsExchangeTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap[tx.status], orderId: tx.transaction_id, + countryCode: null, depositTxid: tx.hash_in, depositAddress: tx.deposit, depositCurrency: tx.coin_from.toUpperCase(), depositAmount: safeParseFloat(tx.deposit_amount), + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: undefined, payoutAddress: tx.withdrawal, payoutCurrency: tx.coin_to.toUpperCase(), diff --git a/src/partners/libertyx.ts b/src/partners/libertyx.ts index aaf11d4a..97b55a16 100644 --- a/src/partners/libertyx.ts +++ b/src/partners/libertyx.ts @@ -86,10 +86,14 @@ export function processLibertyxTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.date_us_eastern, + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency: 'USD', depositAmount: tx.all_transactions_usd_sum, + direction: 'buy', + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: 'BTC', diff --git a/src/partners/lifi.ts b/src/partners/lifi.ts index 1ae8607f..fd3ab40d 100644 --- a/src/partners/lifi.ts +++ b/src/partners/lifi.ts @@ -179,10 +179,14 @@ export function processLifiTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap[tx.status], orderId: tx.sending.txHash, + countryCode: null, depositTxid: tx.sending.txHash, depositAddress: undefined, depositCurrency: depositToken.symbol, depositAmount, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: undefined, payoutAddress: tx.toAddress, payoutCurrency: payoutToken.symbol, diff --git a/src/partners/moonpay.ts b/src/partners/moonpay.ts index 110644d7..24566765 100644 --- a/src/partners/moonpay.ts +++ b/src/partners/moonpay.ts @@ -1,7 +1,20 @@ -import { asArray, asNumber, asObject, asString, asUnknown } from 'cleaners' +import { + asArray, + asNumber, + asObject, + asOptional, + asString, + asUnknown +} from 'cleaners' import fetch from 'node-fetch' -import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' +import { + FiatPaymentType, + PartnerPlugin, + PluginParams, + PluginResult, + StandardTx +} from '../types' import { datelog } from '../util' const asMoonpayCurrency = asObject({ @@ -12,16 +25,18 @@ const asMoonpayCurrency = asObject({ }) const asMoonpayTx = asObject({ - cryptoTransactionId: asString, + baseCurrency: asMoonpayCurrency, baseCurrencyAmount: asNumber, - walletAddress: asString, - quoteCurrencyAmount: asNumber, - createdAt: asString, - id: asString, baseCurrencyId: asString, + country: asString, + createdAt: asString, + cryptoTransactionId: asString, currencyId: asString, currency: asMoonpayCurrency, - baseCurrency: asMoonpayCurrency + id: asString, + paymentMethod: asOptional(asString), + quoteCurrencyAmount: asNumber, + walletAddress: asString }) type MoonpayTx = ReturnType @@ -111,10 +126,15 @@ export function processMoonpayTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.id, + + countryCode: tx.country, depositTxid: undefined, depositAddress: undefined, depositCurrency: tx.baseCurrency.code.toUpperCase(), depositAmount: tx.baseCurrencyAmount, + direction: 'buy', + exchangeType: 'fiat', + paymentType: getFiatPaymentType(tx), payoutTxid: tx.cryptoTransactionId, payoutAddress: tx.walletAddress, payoutCurrency: tx.currency.code.toUpperCase(), @@ -126,3 +146,41 @@ export function processMoonpayTx(rawTx: unknown): StandardTx { } return standardTx } + +function getFiatPaymentType(tx: MoonpayTx): FiatPaymentType | null { + switch (tx.paymentMethod) { + case undefined: + return null + case 'ach_bank_transfer': + return 'ach' + case 'apple_pay': + return 'applepay' + case 'credit_debit_card': + return 'credit' + case 'gbp_open_banking_payment': + return 'fasterpayments' + case 'google_pay': + return 'googlepay' + case 'mobile_wallet': + // Idk? + return null + case 'moonpay_balance': + // Idk? + return null + case 'paypal': + return 'paypal' + case 'pix_instant_payment': + return 'pix' + case 'sepa_bank_transfer': + return 'sepa' + case 'venmo': + return 'venmo' + case 'yellow_card_bank_transfer': + // Idk? + return null + default: + throw new Error( + `Unknown payment method: ${tx.paymentMethod} for ${tx.id}` + ) + } +} diff --git a/src/partners/paybis.ts b/src/partners/paybis.ts index f26134b2..ebc0c9f8 100644 --- a/src/partners/paybis.ts +++ b/src/partners/paybis.ts @@ -1,7 +1,9 @@ import { asArray, asDate, + asEither, asMaybe, + asNull, asNumber, asObject, asOptional, @@ -14,6 +16,7 @@ import URL from 'url-parse' import { asStandardPluginParams, EDGE_APP_START_DATE, + FiatPaymentType, PartnerPlugin, PluginParams, PluginResult, @@ -36,15 +39,15 @@ const asCurrency = asObject({ amount: asAmount, currency: asCurrencyCode }) -// const asUserCountry = asObject({ -// name: asString, -// code: asString -// }) -// const asUser = asObject({ -// id: asString, -// email: asString, -// country: asUserCountry -// }) +const asUserCountry = asObject({ + name: asString, + code: asString +}) +const asUser = asObject({ + id: asString, + email: asString, + country: asEither(asUserCountry, asNull) +}) // const asExchangeRate = asObject({ // currencyTo: asCurrency, // currencyFrom: asCurrency @@ -68,7 +71,7 @@ const asCurrency = asObject({ // blockchain: asOptional(asBlockchain) // }) const asFromToStructure = asObject({ - // name: asString, + name: asString, // asset: asOptional(asCurrencyDetail), address: asOptional(asString) // destinationTag: asOptional(asString) @@ -91,7 +94,8 @@ const asAmounts = asObject({ // flow: asString, // createdAt: asDate // }) -const asTransaction = asObject({ +type PaybisTx = ReturnType +const asPaybisTx = asObject({ id: asString, gateway: asValue('crypto_to_fiat', 'fiat_to_crypto'), status: asString, @@ -103,9 +107,9 @@ const asTransaction = asObject({ createdAt: asDate, // paidAt: asOptional(asDate), // completedAt: asOptional(asDate), - amounts: asAmounts + amounts: asAmounts, // fees: asFees, - // user: asUser, + user: asUser // request: asRequest }) const asMeta = asObject({ @@ -260,7 +264,7 @@ export const paybis: PartnerPlugin = { } export function processPaybisTx(rawTx: unknown): StandardTx { - const tx = asTransaction(rawTx) + const tx = asPaybisTx(rawTx) const { amounts, createdAt, gateway, hash, id } = tx const { spentOriginal, receivedOriginal } = amounts @@ -271,15 +275,21 @@ export function processPaybisTx(rawTx: unknown): StandardTx { const depositTxid = gateway === 'crypto_to_fiat' ? hash : undefined const payoutTxid = gateway === 'fiat_to_crypto' ? hash : undefined + const direction = (gateway === 'fiat_to_crypto') == null ? 'buy' : 'sell' + const standardTx: StandardTx = { status: statusMap[tx.status], orderId: id, + countryCode: tx.user.country?.code ?? null, depositTxid, depositAddress: undefined, depositCurrency: spentOriginal.currency, depositAmount, + direction, + exchangeType: 'fiat', + paymentType: getFiatPaymentType(tx, direction), payoutTxid, - payoutAddress: tx.to.address ?? undefined, + payoutAddress: tx.to.address, payoutCurrency: receivedOriginal.currency, payoutAmount, timestamp, @@ -289,3 +299,45 @@ export function processPaybisTx(rawTx: unknown): StandardTx { } return standardTx } + +function getFiatPaymentType( + tx: PaybisTx, + direction: 'buy' | 'sell' +): FiatPaymentType | null { + const name = direction === 'buy' ? tx.from.name : tx.to.name + switch (name) { + case undefined: + return null + case 'AstroPay': + // Idk? + return null + case 'Credit/Debit Card': + return 'credit' + case 'FPX': + // Idk? + return null + case 'Giropay': + // Idk? + return null + case 'Neteller': + // Idk? + return null + case 'Online Banking': + return 'colombiabank' + case 'PIX': + return 'pix' + case 'Revolut Pay': + return 'revolut' + case 'SEPA Bank Transfer': + return 'sepa' + case 'SPEI Bank Transfer': + return 'spei' + case 'SWIFT Bank Transfer': + return 'mexicobank' + case 'Skrill': + // Idk? + return null + default: + throw new Error(`Unknown payment method: ${name} for ${tx.id}`) + } +} diff --git a/src/partners/paytrie.ts b/src/partners/paytrie.ts index 496a43ec..2cb16416 100644 --- a/src/partners/paytrie.ts +++ b/src/partners/paytrie.ts @@ -82,10 +82,14 @@ export function processPaytrieTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: order.inputTXID, + countryCode: null, // No records of paytrie in the DB to determine depositTxid: undefined, depositAddress: order.inputAddress, depositCurrency: order.inputCurrency, depositAmount: order.inputAmount, + direction: null, // No records of paytrie in the DB to determine + exchangeType: 'fiat', // IDK what paytrie is, but I assume it's a fiat exchange + paymentType: null, payoutTxid: undefined, payoutAddress: order.outputAddress, payoutCurrency: order.outputCurrency, diff --git a/src/partners/safello.ts b/src/partners/safello.ts index 3211d619..f4feb6be 100644 --- a/src/partners/safello.ts +++ b/src/partners/safello.ts @@ -87,10 +87,14 @@ export function processSafelloTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.id, + countryCode: null, depositTxid: undefined, depositAddress: undefined, depositCurrency: tx.currency, depositAmount: tx.amount, + direction: 'buy', + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: tx.cryptoCurrency, diff --git a/src/partners/shapeshift.ts b/src/partners/shapeshift.ts index 6530b28c..af05144b 100644 --- a/src/partners/shapeshift.ts +++ b/src/partners/shapeshift.ts @@ -88,10 +88,14 @@ export function processShapeshiftTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.orderId, + countryCode: null, depositTxid: tx.inputTXID, depositAddress: tx.inputAddress, depositCurrency: tx.inputCurrency, depositAmount: tx.inputAmount, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.outputTXID, payoutAddress: tx.outputAddress, payoutCurrency: tx.outputCurrency, diff --git a/src/partners/sideshift.ts b/src/partners/sideshift.ts index 08113e6c..e6b07efe 100644 --- a/src/partners/sideshift.ts +++ b/src/partners/sideshift.ts @@ -179,10 +179,14 @@ export function processSideshiftTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: statusMap[tx.status], orderId: tx.id, + countryCode: null, depositTxid: undefined, depositAddress, depositCurrency: tx.depositAsset, depositAmount: Number(tx.invoiceAmount), + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: undefined, payoutAddress: tx.settleAddress.address, payoutCurrency: tx.settleAsset, diff --git a/src/partners/simplex.ts b/src/partners/simplex.ts index 9c085125..9e6b4dec 100644 --- a/src/partners/simplex.ts +++ b/src/partners/simplex.ts @@ -12,6 +12,7 @@ import { import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' import { safeParseFloat } from '../util' +import { isFiatCurrency } from '../util/fiatCurrency' const asSimplexTx = asObject({ amount_usd: asString, @@ -19,6 +20,7 @@ const asSimplexTx = asObject({ fiat_total_amount: asString, created_at: asNumber, order_id: asString, + country: asString, crypto_currency: asString, currency: asString }) @@ -140,16 +142,34 @@ export const simplex: PartnerPlugin = { export function processSimplexTx(rawTx: unknown): StandardTx { const tx = asSimplexTx(rawTx) + + const depositCurrency = tx.currency + const payoutCurrency = tx.crypto_currency + const isDepositFiat = isFiatCurrency(depositCurrency) + const isPayoutFiat = isFiatCurrency(payoutCurrency) + + const direction = isDepositFiat ? 'buy' : isPayoutFiat ? 'sell' : undefined + + if (direction == null) { + throw new Error( + `Unknown direction for tx ${tx.order_id}; no fiat currency in trade from ${depositCurrency} to ${payoutCurrency}` + ) + } + const standardTx: StandardTx = { status: 'complete', orderId: tx.order_id, + countryCode: tx.country, depositTxid: undefined, depositAddress: undefined, - depositCurrency: tx.currency, + depositCurrency, depositAmount: safeParseFloat(tx.fiat_total_amount), + direction, + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, - payoutCurrency: tx.crypto_currency, + payoutCurrency, payoutAmount: safeParseFloat(tx.amount_crypto), timestamp: tx.created_at, isoDate: new Date(tx.created_at * 1000).toISOString(), diff --git a/src/partners/swapuz.ts b/src/partners/swapuz.ts index 710ebcc5..e187c993 100644 --- a/src/partners/swapuz.ts +++ b/src/partners/swapuz.ts @@ -16,6 +16,7 @@ import { Status } from '../types' import { datelog, retryFetch, smartIsoDateFromTimestamp } from '../util' +import { isFiatCurrency } from '../util/fiatCurrency' const asSwapuzLogin = asObject({ result: asObject({ @@ -172,10 +173,14 @@ export function processSwapuzTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status, orderId: tx.uid, + countryCode: null, depositTxid: tx.dTxId ?? tx.depositTransactionID, depositCurrency: tx.from.toUpperCase(), depositAddress: tx.depositAddress, depositAmount: tx.amount, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.wTxId ?? tx.withdrawalTransactionID, payoutCurrency: tx.to.toUpperCase(), payoutAddress: undefined, diff --git a/src/partners/switchain.ts b/src/partners/switchain.ts index 30d5d5d1..7118aaab 100644 --- a/src/partners/switchain.ts +++ b/src/partners/switchain.ts @@ -115,10 +115,14 @@ export function processSwitchainTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.id, + countryCode: null, depositTxid: tx.depositTxId, depositAddress: tx.depositAddress, depositCurrency: pair[0].toUpperCase(), depositAmount: safeParseFloat(tx.amountFrom), + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: tx.withdrawTxId, payoutAddress: tx.withdrawAddress, payoutCurrency: pair[1].toUpperCase(), diff --git a/src/partners/thorchain.ts b/src/partners/thorchain.ts index fd8b7b06..e93ed2d1 100644 --- a/src/partners/thorchain.ts +++ b/src/partners/thorchain.ts @@ -200,10 +200,14 @@ export function processThorchainTx(rawTx: unknown): StandardTx { const standardTx: StandardTx = { status: 'complete', orderId: tx.in[0].txID, + countryCode: null, depositTxid: tx.in[0].txID, depositAddress: undefined, depositCurrency: asset.toUpperCase(), depositAmount, + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: txOut?.txID, payoutAddress: txOut?.address, payoutCurrency, diff --git a/src/partners/totle.ts b/src/partners/totle.ts index b829a174..fc2815b6 100644 --- a/src/partners/totle.ts +++ b/src/partners/totle.ts @@ -390,6 +390,7 @@ export async function queryTotle( const ssTx: StandardTx = { status: 'complete', orderId: receipt.transactionHash, + countryCode: null, depositTxid: receipt.transactionHash, depositAddress: receipt.from, depositCurrency: sourceToken.symbol, @@ -401,6 +402,9 @@ export async function queryTotle( 10 ) ), + direction: null, + exchangeType: 'swap', + paymentType: null, payoutTxid: receipt.transactionHash, payoutAddress: receipt.to, payoutCurrency: destinationToken.symbol, diff --git a/src/partners/transak.ts b/src/partners/transak.ts index e620ca9c..3c809519 100644 --- a/src/partners/transak.ts +++ b/src/partners/transak.ts @@ -10,7 +10,13 @@ import { } from 'cleaners' import fetch from 'node-fetch' -import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' +import { + FiatPaymentType, + PartnerPlugin, + PluginParams, + PluginResult, + StandardTx +} from '../types' import { datelog } from '../util' const PAGE_LIMIT = 100 @@ -22,10 +28,12 @@ const asTransakOrder = asObject({ fromWalletAddress: asOptional(asEither(asBoolean, asString)), fiatCurrency: asString, fiatAmount: asNumber, + isBuyOrSell: asString, walletAddress: asString, cryptoCurrency: asString, cryptoAmount: asNumber, - completedAt: asString + completedAt: asString, + paymentOptionId: asString }) type TransakOrder = ReturnType @@ -102,13 +110,29 @@ export function processTransakTx(rawTx: unknown): StandardTx { const date = new Date(tx.completedAt) const depositAddress = typeof tx.fromWalletAddress === 'string' ? tx.fromWalletAddress : undefined + + const direction = + tx.isBuyOrSell === 'BUY' + ? 'buy' + : tx.isBuyOrSell === 'SELL' + ? 'sell' + : undefined + + if (direction == null) { + throw new Error(`Unexpected isBuyOrSell '${tx.isBuyOrSell}' for ${tx.id}`) + } + const standardTx: StandardTx = { status: 'complete', orderId: tx.id, + countryCode: null, depositTxid: undefined, depositAddress, depositCurrency: tx.fiatCurrency, depositAmount: tx.fiatAmount, + direction, + exchangeType: 'fiat', + paymentType: getFiatPaymentType(tx), payoutTxid: undefined, payoutAddress: tx.walletAddress, payoutCurrency: tx.cryptoCurrency, @@ -120,3 +144,18 @@ export function processTransakTx(rawTx: unknown): StandardTx { } return standardTx } + +function getFiatPaymentType(tx: TransakOrder): FiatPaymentType | null { + switch (tx.paymentOptionId) { + case 'mobikwik_wallet': + return null + case 'neft_bank_transfer': + return null + case 'upi': + return null + default: + throw new Error( + `Unknown payment method: ${tx.paymentOptionId} for ${tx.id}` + ) + } +} diff --git a/src/partners/wyre.ts b/src/partners/wyre.ts index 417bbfa8..eea0231d 100644 --- a/src/partners/wyre.ts +++ b/src/partners/wyre.ts @@ -3,6 +3,7 @@ import fetch from 'node-fetch' import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' import { safeParseFloat } from '../util' +import { isFiatCurrency } from '../util/fiatCurrency' const asWyreTx = asObject({ id: asString, @@ -100,16 +101,33 @@ export function processWyreTx(rawTx: unknown): StandardTx { const date = new Date(tx.createdAt) const dateMs = date.getTime() + const depositCurrency = tx.sourceCurrency + const payoutCurrency = tx.destCurrency + const isDepositFiat = isFiatCurrency(depositCurrency) + const isPayoutFiat = isFiatCurrency(payoutCurrency) + + const direction = isDepositFiat ? 'buy' : isPayoutFiat ? 'sell' : undefined + + if (direction == null) { + throw new Error( + `Unknown direction for tx ${tx.id}; no fiat currency in trade from ${depositCurrency} to ${payoutCurrency}` + ) + } + const standardTx: StandardTx = { status: 'complete', orderId: tx.id, + countryCode: null, depositTxid: undefined, depositAddress: undefined, - depositCurrency: tx.sourceCurrency, + depositCurrency, depositAmount: safeParseFloat(tx.sourceAmount), + direction, + exchangeType: 'fiat', + paymentType: null, payoutTxid: undefined, payoutAddress: undefined, - payoutCurrency: tx.destCurrency, + payoutCurrency, payoutAmount: safeParseFloat(tx.destAmount), timestamp: dateMs / 1000, isoDate: date.toISOString(), diff --git a/src/partners/xanpool.ts b/src/partners/xanpool.ts index 9fd569ed..c0348291 100644 --- a/src/partners/xanpool.ts +++ b/src/partners/xanpool.ts @@ -28,6 +28,7 @@ const asXanpoolTx = asObject({ cryptoPriceUsd: asNumber, blockchainTxId: asOptional(asString), wallet: asOptional(asString), + userCountry: asString, depositWallets: asOptional(asMap(asString)), createdAt: asString }) @@ -125,10 +126,14 @@ export function processXanpoolTx(rawTx: unknown): StandardTx { return { status: tx.status === 'completed' ? 'complete' : 'expired', orderId: tx.id, + countryCode: tx.userCountry, depositTxid: undefined, depositAddress: undefined, depositCurrency: tx.currency, depositAmount: tx.fiat, + direction: 'buy', + exchangeType: 'fiat', + paymentType: null, // Or whatever tx.method === 'paynow' means? payoutTxid: tx.blockchainTxId, payoutAddress: tx.wallet, payoutCurrency: tx.cryptoCurrency, @@ -143,10 +148,14 @@ export function processXanpoolTx(rawTx: unknown): StandardTx { return { status: tx.status === 'completed' ? 'complete' : 'expired', orderId: tx.id, + countryCode: tx.userCountry, depositTxid: tx.blockchainTxId, depositAddress: Object.values(tx.depositWallets ?? {})[0], depositCurrency: tx.cryptoCurrency, depositAmount: tx.crypto, + direction: 'sell', + exchangeType: 'fiat', + paymentType: null, // Or whatever tx.method === 'paynow' means? payoutTxid: undefined, payoutAddress: undefined, payoutCurrency: tx.currency, @@ -158,6 +167,6 @@ export function processXanpoolTx(rawTx: unknown): StandardTx { rawTx } } else { - throw new Error(`Invalid tx type ${tx.type}`) + throw new Error(`Invalid tx type ${tx.type} for ${tx.id}`) } } diff --git a/src/types.ts b/src/types.ts index 90a7d963..69075d13 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,8 @@ import { asArray, + asEither, asMap, + asNull, asNumber, asObject, asOptional, @@ -46,12 +48,52 @@ const asSafeNumber = (raw: any): number => { return asNumber(raw) } -const standardTxFields = { +/** A null direction is for swap exchange types. */ +const asDirection = asEither(asValue('buy', 'sell'), asNull) + +/** + * Exact copy from GUI (src/plugins/gui/fiatPluginTypes.ts); please keep it + * matching + */ +const asFiatPaymentType = asValue( + 'ach', + 'applepay', + 'colombiabank', + 'credit', + 'directtobank', + 'fasterpayments', + 'googlepay', + 'iach', + 'ideal', + 'interac', + 'iobank', + 'mexicobank', + 'payid', + 'paypal', + 'pix', + 'pse', + 'revolut', + 'sepa', + 'spei', + 'turkishbank', + 'venmo', + 'wire' +) +export type FiatPaymentType = ReturnType + +/** The type of exchange that the partner is. A 'fiat' type means on/off ramp. */ +const asExchangeType = asValue('fiat', 'swap') + +export const asStandardTx = asObject({ orderId: asString, + countryCode: asEither(asString, asNull), depositTxid: asOptional(asString), depositAddress: asOptional(asString), depositCurrency: asString, depositAmount: asSafeNumber, + direction: asDirection, + exchangeType: asExchangeType, + paymentType: asEither(asFiatPaymentType, asNull), payoutTxid: asOptional(asString), payoutAddress: asOptional(asString), payoutCurrency: asString, @@ -61,13 +103,13 @@ const standardTxFields = { timestamp: asNumber, usdValue: asNumber, rawTx: asUnknown -} +}) + export const asDbTx = asObject({ - ...standardTxFields, + ...asStandardTx.shape, _id: asOptional(asString), _rev: asOptional(asString) }) -export const asStandardTx = asObject(standardTxFields) export const asProgressSettings = asObject({ _id: asOptional(asString), diff --git a/src/util/fiatCurrency.ts b/src/util/fiatCurrency.ts new file mode 100644 index 00000000..ab7f467d --- /dev/null +++ b/src/util/fiatCurrency.ts @@ -0,0 +1,19 @@ +const fiatCurrencyCodes = [ + 'USD', + 'EUR', + 'JPY', + 'GBP', + 'AUD', + 'CAD', + 'CHF', + 'CNY', + 'SEK', + 'NZD', + 'KRW', + 'SGD', + 'NOK' +] + +export function isFiatCurrency(currencyCode: string): boolean { + return fiatCurrencyCodes.includes(currencyCode) +}