1- import { asArray , asNumber , asObject , asString , asUnknown } from 'cleaners'
1+ import {
2+ asArray ,
3+ asDate ,
4+ asNumber ,
5+ asObject ,
6+ asString ,
7+ asUnknown
8+ } from 'cleaners'
29import fetch from 'node-fetch'
310
4- import { PartnerPlugin , PluginParams , PluginResult , StandardTx } from '../types'
11+ import {
12+ asStandardPluginParams ,
13+ EDGE_APP_START_DATE ,
14+ PartnerPlugin ,
15+ PluginParams ,
16+ PluginResult ,
17+ StandardTx
18+ } from '../types'
519import { datelog } from '../util'
620
721const asMoonpayCurrency = asObject ( {
@@ -16,23 +30,35 @@ const asMoonpayTx = asObject({
1630 baseCurrencyAmount : asNumber ,
1731 walletAddress : asString ,
1832 quoteCurrencyAmount : asNumber ,
19- createdAt : asString ,
33+ createdAt : asDate ,
2034 id : asString ,
2135 baseCurrencyId : asString ,
22- currencyId : asString ,
2336 currency : asMoonpayCurrency ,
2437 baseCurrency : asMoonpayCurrency
2538} )
2639
40+ const asMoonpaySellTx = asObject ( {
41+ baseCurrencyAmount : asNumber ,
42+ quoteCurrencyAmount : asNumber ,
43+ createdAt : asDate ,
44+ id : asString ,
45+ baseCurrencyId : asString ,
46+ depositHash : asString ,
47+ quoteCurrency : asMoonpayCurrency ,
48+ baseCurrency : asMoonpayCurrency
49+ } )
50+
2751type MoonpayTx = ReturnType < typeof asMoonpayTx >
52+ type MoonpaySellTx = ReturnType < typeof asMoonpaySellTx >
2853
2954const asMoonpayRawTx = asObject ( {
3055 status : asString
3156} )
3257
3358const asMoonpayResult = asArray ( asUnknown )
3459
35- const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 5
60+ const PARTNER_START_DATE = '2024-06-17T00:00:00.000Z'
61+ const QUERY_LOOKBACK = 1000 * 60 * 60 * 24 * 7
3662const PER_REQUEST_LIMIT = 50
3763
3864export async function queryMoonpay (
@@ -41,80 +67,167 @@ export async function queryMoonpay(
4167 const ssFormatTxs : StandardTx [ ] = [ ]
4268
4369 let headers
44- let latestTimestamp = 0
45- if ( typeof pluginParams . settings . latestTimestamp === 'number' ) {
46- latestTimestamp = pluginParams . settings . latestTimestamp
70+ const { apiKeys, settings } = asStandardPluginParams ( pluginParams )
71+ let { latestIsoDate } = settings
72+ if ( latestIsoDate === EDGE_APP_START_DATE ) {
73+ latestIsoDate = PARTNER_START_DATE
4774 }
75+ const { apiKey } = pluginParams . apiKeys
4876
49- const apiKey = pluginParams . apiKeys . apiKey
5077 if ( typeof apiKey === 'string' ) {
5178 headers = {
5279 Authorization : `Api-Key ${ apiKey } `
5380 }
5481 } else {
5582 return {
56- settings : { latestTimestamp : latestTimestamp } ,
83+ settings : { latestIsoDate } ,
5784 transactions : [ ]
5885 }
5986 }
6087
61- if ( latestTimestamp > QUERY_LOOKBACK ) {
62- latestTimestamp -= QUERY_LOOKBACK
63- }
64- let done = false
65- let offset = 0
66- let newestTimestamp = latestTimestamp
67- while ( ! done ) {
68- const url = `https://api.moonpay.io/v1/transactions?limit=${ PER_REQUEST_LIMIT } &offset=${ offset } `
69- const result = await fetch ( url , {
70- method : 'GET' ,
71- headers
72- } )
73- const txs = asMoonpayResult ( await result . json ( ) )
74- // cryptoTransactionId is a duplicate among other transactions sometimes
75- // in bulk update it throws an error for document update conflict because of this.
76-
77- for ( const rawtx of txs ) {
78- if ( asMoonpayRawTx ( rawtx ) . status === 'completed' ) {
79- let tx : MoonpayTx
80- try {
81- tx = asMoonpayTx ( rawtx )
82- } catch ( e ) {
83- datelog ( e )
84- datelog ( rawtx )
85- throw e
88+ // Make endDate a week after the query date
89+ let queryIsoDate = new Date (
90+ new Date ( latestIsoDate ) . getTime ( ) - QUERY_LOOKBACK
91+ ) . toISOString ( )
92+
93+ const isoNow = new Date ( ) . toISOString ( )
94+
95+ try {
96+ do {
97+ console . log ( `Querying Moonpay from ${ queryIsoDate } to ${ latestIsoDate } ` )
98+ let offset = 0
99+
100+ while ( true ) {
101+ const url = `https://api.moonpay.io/v3/sell_transactions?limit=${ PER_REQUEST_LIMIT } &offset=${ offset } &startDate=${ queryIsoDate } &endDate=${ latestIsoDate } `
102+ const result = await fetch ( url , {
103+ method : 'GET' ,
104+ headers
105+ } )
106+ const txs = asMoonpayResult ( await result . json ( ) )
107+
108+ for ( const rawtx of txs ) {
109+ if ( asMoonpayRawTx ( rawtx ) . status === 'completed' ) {
110+ let tx : MoonpaySellTx
111+ try {
112+ tx = asMoonpaySellTx ( rawtx )
113+ } catch ( e ) {
114+ datelog ( e )
115+ datelog ( rawtx )
116+ throw e
117+ }
118+
119+ const isoDate = tx . createdAt . toISOString ( )
120+ const timestamp = tx . createdAt . getTime ( )
121+ const ssTx : StandardTx = {
122+ status : 'complete' ,
123+ orderId : tx . id ,
124+ depositTxid : tx . depositHash ,
125+ depositAddress : undefined ,
126+ depositCurrency : tx . baseCurrency . code . toUpperCase ( ) ,
127+ depositAmount : tx . baseCurrencyAmount ,
128+ payoutTxid : undefined ,
129+ payoutAddress : undefined ,
130+ payoutCurrency : tx . quoteCurrency . code . toUpperCase ( ) ,
131+ payoutAmount : tx . quoteCurrencyAmount ,
132+ timestamp : timestamp / 1000 ,
133+ isoDate,
134+ usdValue : - 1 ,
135+ rawTx : rawtx
136+ }
137+ ssFormatTxs . push ( ssTx )
138+ }
139+ }
140+
141+ if ( txs . length > 0 ) {
142+ console . log (
143+ `Moonpay sell txs ${ txs . length } : ${ JSON . stringify (
144+ txs . slice ( - 1 )
145+ ) . slice ( 0 , 100 ) } `
146+ )
86147 }
87148
88- const date = new Date ( tx . createdAt )
89- const timestamp = date . getTime ( )
90- const ssTx : StandardTx = {
91- status : 'complete' ,
92- orderId : tx . id ,
93- depositTxid : undefined ,
94- depositAddress : undefined ,
95- depositCurrency : tx . baseCurrency . code . toUpperCase ( ) ,
96- depositAmount : tx . baseCurrencyAmount ,
97- payoutTxid : tx . cryptoTransactionId ,
98- payoutAddress : tx . walletAddress ,
99- payoutCurrency : tx . currency . code . toUpperCase ( ) ,
100- payoutAmount : tx . quoteCurrencyAmount ,
101- timestamp : timestamp / 1000 ,
102- isoDate : tx . createdAt ,
103- usdValue : - 1 ,
104- rawTx : rawtx
149+ if ( txs . length < PER_REQUEST_LIMIT ) {
150+ break
105151 }
106- ssFormatTxs . push ( ssTx )
107- done = latestTimestamp > timestamp || txs . length < PER_REQUEST_LIMIT
108- newestTimestamp =
109- newestTimestamp > timestamp ? newestTimestamp : timestamp
152+
153+ offset += PER_REQUEST_LIMIT
110154 }
111- }
112155
113- offset += PER_REQUEST_LIMIT
156+ offset = 0
157+ while ( true ) {
158+ const url = `https://api.moonpay.io/v1/transactions?limit=${ PER_REQUEST_LIMIT } &offset=${ offset } &startDate=${ queryIsoDate } &endDate=${ latestIsoDate } `
159+ const result = await fetch ( url , {
160+ method : 'GET' ,
161+ headers
162+ } )
163+ const txs = asMoonpayResult ( await result . json ( ) )
164+ // cryptoTransactionId is a duplicate among other transactions sometimes
165+ // in bulk update it throws an error for document update conflict because of this.
166+
167+ for ( const rawtx of txs ) {
168+ if ( asMoonpayRawTx ( rawtx ) . status === 'completed' ) {
169+ let tx : MoonpayTx
170+ try {
171+ tx = asMoonpayTx ( rawtx )
172+ } catch ( e ) {
173+ datelog ( e )
174+ datelog ( rawtx )
175+ throw e
176+ }
177+
178+ const isoDate = tx . createdAt . toISOString ( )
179+ const timestamp = tx . createdAt . getTime ( )
180+ const ssTx : StandardTx = {
181+ status : 'complete' ,
182+ orderId : tx . id ,
183+ depositTxid : undefined ,
184+ depositAddress : undefined ,
185+ depositCurrency : tx . baseCurrency . code . toUpperCase ( ) ,
186+ depositAmount : tx . baseCurrencyAmount ,
187+ payoutTxid : tx . cryptoTransactionId ,
188+ payoutAddress : tx . walletAddress ,
189+ payoutCurrency : tx . currency . code . toUpperCase ( ) ,
190+ payoutAmount : tx . quoteCurrencyAmount ,
191+ timestamp : timestamp / 1000 ,
192+ isoDate,
193+ usdValue : - 1 ,
194+ rawTx : rawtx
195+ }
196+ ssFormatTxs . push ( ssTx )
197+ }
198+ }
199+ if ( txs . length > 0 ) {
200+ console . log (
201+ `Moonpay buy txs ${ txs . length } : ${ JSON . stringify (
202+ txs . slice ( - 1 )
203+ ) . slice ( 0 , 100 ) } `
204+ )
205+ }
206+
207+ if ( txs . length < PER_REQUEST_LIMIT ) {
208+ break
209+ }
210+
211+ offset += PER_REQUEST_LIMIT
212+ }
213+ queryIsoDate = latestIsoDate
214+ latestIsoDate = new Date (
215+ new Date ( latestIsoDate ) . getTime ( ) + QUERY_LOOKBACK
216+ ) . toISOString ( )
217+ } while ( isoNow > latestIsoDate )
218+ latestIsoDate = isoNow
219+ } catch ( e ) {
220+ datelog ( e )
221+ console . log ( `Moonpay error: ${ e } ` )
222+ console . log ( `Saving progress up until ${ queryIsoDate } ` )
223+
224+ // Set the latestIsoDate to the queryIsoDate so that the next query will
225+ // query the same time range again since we had a failure in that time range
226+ latestIsoDate = queryIsoDate
114227 }
115228
116229 const out : PluginResult = {
117- settings : { latestTimestamp : newestTimestamp } ,
230+ settings : { latestIsoDate } ,
118231 transactions : ssFormatTxs
119232 }
120233 return out
0 commit comments