Skip to content

Commit efd5290

Browse files
committed
Swapter plugin integration
1 parent 87447b7 commit efd5290

2 files changed

Lines changed: 238 additions & 0 deletions

File tree

src/partners/swapter.ts

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
import {
2+
asArray,
3+
asMaybe,
4+
asNumber,
5+
asObject,
6+
asString,
7+
asUnknown,
8+
asValue
9+
} from 'cleaners'
10+
11+
import {
12+
asStandardPluginParams,
13+
PartnerPlugin,
14+
PluginParams,
15+
PluginResult,
16+
StandardTx,
17+
Status
18+
} from '../types'
19+
import { datelog, retryFetch, snooze } from '../util'
20+
21+
const asSwapterStatus = asMaybe(
22+
asValue(
23+
'Waiting',
24+
'Confirmation',
25+
'Exchanging',
26+
'Sending',
27+
'Success',
28+
'Frozen',
29+
'Refunded',
30+
'Overdue',
31+
'Suspended'
32+
),
33+
'other'
34+
)
35+
36+
const asSwapterTx = asObject({
37+
info: asObject({
38+
uid: asString,
39+
status: asSwapterStatus,
40+
type: asString,
41+
link: asString,
42+
equivalent: asNumber
43+
}),
44+
deposit: asObject({
45+
coin: asString,
46+
network: asString,
47+
amount: asNumber,
48+
actual: asMaybe(asNumber),
49+
address: asString,
50+
memo: asMaybe(asString)
51+
}),
52+
withdraw: asObject({
53+
coin: asString,
54+
network: asString,
55+
amount: asNumber,
56+
address: asString,
57+
memo: asMaybe(asString)
58+
}),
59+
time: asObject({
60+
create: asNumber,
61+
confirmation: asMaybe(asNumber),
62+
exchanging: asMaybe(asNumber),
63+
send: asMaybe(asNumber),
64+
success: asMaybe(asNumber),
65+
overdue: asMaybe(asNumber)
66+
}),
67+
partner: asObject({
68+
name: asString,
69+
profit: asObject({
70+
amount: asNumber,
71+
percent: asNumber
72+
})
73+
})
74+
})
75+
76+
const asSwapterResult = asObject({
77+
page: asNumber,
78+
total: asNumber,
79+
data: asArray(asUnknown)
80+
})
81+
82+
type SwapterTx = ReturnType<typeof asSwapterTx>
83+
type SwapterStatus = ReturnType<typeof asSwapterStatus>
84+
85+
const MAX_RETRIES = 5
86+
const LIMIT = 200
87+
const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 5 // 5 days
88+
89+
const statusMap: { [key in SwapterStatus]: Status } = {
90+
Waiting: 'pending',
91+
Confirmation: 'pending',
92+
Exchanging: 'pending',
93+
Sending: 'pending',
94+
Success: 'complete',
95+
Overdue: 'expired',
96+
Refunded: 'refunded',
97+
Frozen: 'other',
98+
Suspended: 'other',
99+
other: 'other'
100+
}
101+
102+
export const querySwapter = async (
103+
pluginParams: PluginParams
104+
): Promise<PluginResult> => {
105+
const { settings, apiKeys } = asStandardPluginParams(pluginParams)
106+
const { apiKey } = apiKeys
107+
let latestIsoDate =
108+
typeof settings.latestIsoDate === 'string'
109+
? settings.latestIsoDate
110+
: new Date(0).toISOString()
111+
112+
if (apiKey == null) {
113+
return { settings: { latestIsoDate }, transactions: [] }
114+
}
115+
116+
const standardTxs: StandardTx[] = []
117+
118+
let previousTimestamp = new Date(latestIsoDate).getTime() - QUERY_LOOKBACK
119+
if (previousTimestamp < 0) previousTimestamp = 0
120+
121+
let page = 1
122+
let retry = 0
123+
124+
while (true) {
125+
try {
126+
const response = await retryFetch(
127+
'https://api.swapter.io/personal/exchange/tool-history',
128+
{
129+
method: 'POST',
130+
headers: {
131+
'X-Api-Key': apiKey,
132+
'Content-Type': 'application/json'
133+
},
134+
body: JSON.stringify({
135+
page,
136+
items: LIMIT,
137+
timeFrom: previousTimestamp,
138+
timeTo: Date.now()
139+
})
140+
}
141+
)
142+
143+
if (!response.ok) {
144+
const text = await response.text()
145+
datelog(`Swapter error on page:${page}`)
146+
throw new Error(text)
147+
}
148+
149+
const result = asSwapterResult(await response.json())
150+
const txs = result.data
151+
152+
if (txs.length === 0) break
153+
154+
for (const rawTx of txs) {
155+
const standardTx = processSwapterTx(rawTx)
156+
157+
if (standardTx.isoDate <= latestIsoDate) continue
158+
159+
standardTxs.push(standardTx)
160+
161+
if (standardTx.isoDate > latestIsoDate) {
162+
latestIsoDate = standardTx.isoDate
163+
}
164+
}
165+
166+
datelog(`Swapter page ${page} latestIsoDate ${latestIsoDate}`)
167+
168+
const loaded = page * LIMIT
169+
if (loaded >= result.total) break
170+
171+
page++
172+
retry = 0
173+
} catch (e) {
174+
datelog(e)
175+
176+
retry++
177+
if (retry <= MAX_RETRIES) {
178+
datelog(`Snoozing ${5 * retry}s`)
179+
await snooze(5000 * retry)
180+
} else {
181+
break
182+
}
183+
}
184+
}
185+
186+
return {
187+
settings: { latestIsoDate },
188+
transactions: standardTxs
189+
}
190+
}
191+
192+
export const swapter: PartnerPlugin = {
193+
queryFunc: querySwapter,
194+
pluginName: 'Swapter',
195+
pluginId: 'swapter'
196+
}
197+
198+
export function processSwapterTx(rawTx: unknown): StandardTx {
199+
const tx: SwapterTx = asSwapterTx(rawTx)
200+
201+
const date = new Date(tx.time.create)
202+
const timestamp = tx.time.create / 1000
203+
204+
return {
205+
status: statusMap[tx.info.status],
206+
orderId: tx.info.uid,
207+
countryCode: null,
208+
209+
depositTxid: undefined,
210+
depositAddress: tx.deposit.address,
211+
depositCurrency: tx.deposit.coin.toUpperCase(),
212+
depositChainPluginId: undefined,
213+
depositEvmChainId: undefined,
214+
depositTokenId: undefined,
215+
depositAmount: tx.deposit.actual ?? tx.deposit.amount,
216+
217+
direction: null,
218+
exchangeType: 'swap',
219+
paymentType: null,
220+
221+
payoutTxid: undefined,
222+
payoutAddress: tx.withdraw.address,
223+
payoutCurrency: tx.withdraw.coin.toUpperCase(),
224+
payoutChainPluginId: undefined,
225+
payoutEvmChainId: undefined,
226+
payoutTokenId: undefined,
227+
payoutAmount: tx.withdraw.amount,
228+
229+
timestamp,
230+
isoDate: date.toISOString(),
231+
232+
usdValue: tx.info.equivalent > 0 ? tx.info.equivalent : -1,
233+
234+
rawTx
235+
}
236+
}

src/queryEngine.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { paytrie } from './partners/paytrie'
2626
import { safello } from './partners/safello'
2727
import { sideshift } from './partners/sideshift'
2828
import { simplex } from './partners/simplex'
29+
import { swapter } from './partners/swapter'
2930
import { swapuz } from './partners/swapuz'
3031
import { switchain } from './partners/switchain'
3132
import { maya, thorchain } from './partners/thorchain'
@@ -45,6 +46,7 @@ const plugins = [
4546
bitrefill,
4647
changelly,
4748
changenow,
49+
swapter,
4850
changehero,
4951
exolix,
5052
foxExchange,

0 commit comments

Comments
 (0)