diff --git a/src/bin/partitionStats.ts b/src/bin/partitionStats.ts index 64f3efa8..dad738f6 100644 --- a/src/bin/partitionStats.ts +++ b/src/bin/partitionStats.ts @@ -36,6 +36,7 @@ async function main(partitionName: string): Promise { 'edge_moonpay', 'edge_safello', 'edge_shapeshift', + 'edge_sideshift', 'edge_simplex', 'edge_switchain', 'edge_totle', diff --git a/src/makeSampleRawTxs.js b/src/makeSampleRawTxs.js index 3eee6432..3b2d2722 100644 --- a/src/makeSampleRawTxs.js +++ b/src/makeSampleRawTxs.js @@ -413,6 +413,35 @@ const rawTxs = { type: 'PRECISE', userId: 'e274af63-2a26-45bb-b6a6-202332dd9a2c' }, + sideshift: { + id: 'c421de9317bc18ab76ba', + createdAt: '2020-10-07T15:04:07.846Z', + expiresAt: '2020-10-07T15:19:07.104Z', + depositMethodId: 'bch', + settleMethodId: 'xmr', + depositAsset: 'BCH', + settleAsset: 'XMR', + depositAddress: { + address: 'bitcoincash:qzaz9m2nku3m8hrq55lfgn5sxghzvg4xpsm85cr6h2' + }, + refundAddress: null, + settleAddress: { + address: + '45AZiUFxa5hGwvMChQmdfjNr6HbktbtwhNhpsT4HYosQARXfJ5xWFwrSN7VWyk7ZFoLEYPUq7Qw2RRTTCfEVQFRG75QSUiC' + }, + extra: {}, + affiliateId: 'Coq58fefL', + invoiceAmount: '0.03685881', + depositMin: '0.03685881', + depositMax: '0.03685881', + type: 'fixed', + settleAmount: '0.07353', + settleRate: '1.99491', + settleRateNominal: '219.16435588652246556599', + settleRateSpread: '0.01', + sessionId: null, + quoteId: '1a618edd-f0f3-4c31-9f84-a0555e6974dc' + }, simplex: { transaction_id: 'f64b5e50-d935-11ea-b358-ed6259933d55', created_at: 1596863644.775, diff --git a/src/partners/sideshift.ts b/src/partners/sideshift.ts new file mode 100644 index 00000000..fb7d7dff --- /dev/null +++ b/src/partners/sideshift.ts @@ -0,0 +1,136 @@ +import { asObject, asString } from 'cleaners' +import crypto from 'crypto' +import fetch from 'node-fetch' + +import { PartnerPlugin, PluginParams, PluginResult, StandardTx } from '../types' +import { datelog } from '../util' + +const asSideshiftTx = asObject({ + id: asString, + depositAddress: asObject({ + address: asString + }), + depositAsset: asString, + invoiceAmount: asString, + settleAddress: asObject({ + address: asString + }), + settleAsset: asString, + settleAmount: asString, + createdAt: asString +}) + +const LIMIT = 500 +const QUERY_LOOKBACK = 60 * 60 * 24 * 5 // 5 days + +function affiliateSignature( + affiliateId: string, + affiliateSecret: string, + time: number +): string { + return crypto + .createHmac('sha1', affiliateSecret) + .update(`${affiliateId}${time}`) + .digest('hex') +} + +async function fetchTransactions( + affiliateId: string, + affiliateSecret: string, + offset: number, + limit: number +): Promise { + const time = Date.now() + + const signature = affiliateSignature(affiliateId, affiliateSecret, time) + const url = `https://sideshift.ai/api/affiliate/completedOrders?limit=${limit}&offset=${offset}&affiliateId=${affiliateId}&time=${time}&signature=${signature}` + + try { + const response = await fetch(url) + const orders = await response.json() + + return orders.map(order => { + const tx = asSideshiftTx(order) + + return { + status: 'complete', + orderId: tx.id, + depositTxid: undefined, + depositAddress: tx.depositAddress.address, + depositCurrency: tx.depositAsset.toUpperCase(), + depositAmount: Number(tx.invoiceAmount), + payoutTxid: undefined, + payoutAddress: tx.settleAddress.address, + payoutCurrency: tx.settleAsset.toUpperCase(), + payoutAmount: Number(tx.settleAmount), + timestamp: new Date(tx.createdAt).getTime() / 1000, + isoDate: tx.createdAt, + usdValue: undefined, + rawTx: order + } + }) + } catch (e) { + datelog(e) + throw e + } +} + +export async function querySideshift( + pluginParams: PluginParams +): Promise { + const { + apiKeys: { sideshiftAffiliateId, sideshiftAffiliateSecret } + } = pluginParams + let { + settings: { lastCheckedTimestamp, offset } + } = pluginParams + + if (typeof lastCheckedTimestamp === 'number') { + lastCheckedTimestamp -= QUERY_LOOKBACK + } + + if (!(typeof sideshiftAffiliateSecret === 'string')) { + return { + settings: { lastCheckedTimestamp }, + transactions: [] + } + } + + const txs: StandardTx[] = [] + + let prevMaxTimestamp = 0 + + while (true) { + const newTxs = await fetchTransactions( + sideshiftAffiliateId, + sideshiftAffiliateSecret, + offset, + LIMIT + ) + + txs.push(...newTxs) + + offset += newTxs.length + + const newTxMaxTimestamp = Math.max(...newTxs.map(tx => tx.timestamp)) + + if (newTxMaxTimestamp > prevMaxTimestamp) { + prevMaxTimestamp = newTxMaxTimestamp + } + + if (lastCheckedTimestamp > newTxMaxTimestamp || newTxs.length < LIMIT) { + break + } + } + + return { + settings: { lastCheckedTimestamp: prevMaxTimestamp, offset }, + transactions: txs + } +} + +export const sideshift: PartnerPlugin = { + queryFunc: querySideshift, + pluginName: 'SideShift.ai', + pluginId: 'sideshift' +} diff --git a/src/queryEngine.ts b/src/queryEngine.ts index 103adeb9..2a7bd91b 100644 --- a/src/queryEngine.ts +++ b/src/queryEngine.ts @@ -18,6 +18,7 @@ import { moonpay } from './partners/moonpay' import { paytrie } from './partners/paytrie' import { safello } from './partners/safello' import { shapeshift } from './partners/shapeshift' +import { sideshift } from './partners/sideshift' import { simplex } from './partners/simplex' import { switchain } from './partners/switchain' import { totle } from './partners/totle' @@ -74,6 +75,7 @@ const partners = [ paytrie, safello, shapeshift, + sideshift, switchain, totle, transak,