@nadohq/indexer-client 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +2 -0
  2. package/dist/IndexerBaseClient.cjs +608 -0
  3. package/dist/IndexerBaseClient.cjs.map +1 -0
  4. package/dist/IndexerBaseClient.d.cts +183 -0
  5. package/dist/IndexerBaseClient.d.ts +183 -0
  6. package/dist/IndexerBaseClient.js +605 -0
  7. package/dist/IndexerBaseClient.js.map +1 -0
  8. package/dist/IndexerClient.cjs +368 -0
  9. package/dist/IndexerClient.cjs.map +1 -0
  10. package/dist/IndexerClient.d.cts +50 -0
  11. package/dist/IndexerClient.d.ts +50 -0
  12. package/dist/IndexerClient.js +348 -0
  13. package/dist/IndexerClient.js.map +1 -0
  14. package/dist/dataMappers.cjs +324 -0
  15. package/dist/dataMappers.cjs.map +1 -0
  16. package/dist/dataMappers.d.cts +31 -0
  17. package/dist/dataMappers.d.ts +31 -0
  18. package/dist/dataMappers.js +296 -0
  19. package/dist/dataMappers.js.map +1 -0
  20. package/dist/endpoints.cjs +35 -0
  21. package/dist/endpoints.cjs.map +1 -0
  22. package/dist/endpoints.d.cts +5 -0
  23. package/dist/endpoints.d.ts +5 -0
  24. package/dist/endpoints.js +10 -0
  25. package/dist/endpoints.js.map +1 -0
  26. package/dist/index.cjs +31 -0
  27. package/dist/index.cjs.map +1 -0
  28. package/dist/index.d.cts +18 -0
  29. package/dist/index.d.ts +18 -0
  30. package/dist/index.js +6 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/types/CandlestickPeriod.cjs +42 -0
  33. package/dist/types/CandlestickPeriod.cjs.map +1 -0
  34. package/dist/types/CandlestickPeriod.d.cts +13 -0
  35. package/dist/types/CandlestickPeriod.d.ts +13 -0
  36. package/dist/types/CandlestickPeriod.js +17 -0
  37. package/dist/types/CandlestickPeriod.js.map +1 -0
  38. package/dist/types/IndexerEventType.cjs +19 -0
  39. package/dist/types/IndexerEventType.cjs.map +1 -0
  40. package/dist/types/IndexerEventType.d.cts +3 -0
  41. package/dist/types/IndexerEventType.d.ts +3 -0
  42. package/dist/types/IndexerEventType.js +1 -0
  43. package/dist/types/IndexerEventType.js.map +1 -0
  44. package/dist/types/IndexerLeaderboardType.cjs +19 -0
  45. package/dist/types/IndexerLeaderboardType.cjs.map +1 -0
  46. package/dist/types/IndexerLeaderboardType.d.cts +3 -0
  47. package/dist/types/IndexerLeaderboardType.d.ts +3 -0
  48. package/dist/types/IndexerLeaderboardType.js +1 -0
  49. package/dist/types/IndexerLeaderboardType.js.map +1 -0
  50. package/dist/types/NadoTx.cjs +19 -0
  51. package/dist/types/NadoTx.cjs.map +1 -0
  52. package/dist/types/NadoTx.d.cts +49 -0
  53. package/dist/types/NadoTx.d.ts +49 -0
  54. package/dist/types/NadoTx.js +1 -0
  55. package/dist/types/NadoTx.js.map +1 -0
  56. package/dist/types/clientTypes.cjs +19 -0
  57. package/dist/types/clientTypes.cjs.map +1 -0
  58. package/dist/types/clientTypes.d.cts +467 -0
  59. package/dist/types/clientTypes.d.ts +467 -0
  60. package/dist/types/clientTypes.js +1 -0
  61. package/dist/types/clientTypes.js.map +1 -0
  62. package/dist/types/collateralEventType.cjs +19 -0
  63. package/dist/types/collateralEventType.cjs.map +1 -0
  64. package/dist/types/collateralEventType.d.cts +3 -0
  65. package/dist/types/collateralEventType.d.ts +3 -0
  66. package/dist/types/collateralEventType.js +1 -0
  67. package/dist/types/collateralEventType.js.map +1 -0
  68. package/dist/types/index.cjs +41 -0
  69. package/dist/types/index.cjs.map +1 -0
  70. package/dist/types/index.d.cts +13 -0
  71. package/dist/types/index.d.ts +13 -0
  72. package/dist/types/index.js +11 -0
  73. package/dist/types/index.js.map +1 -0
  74. package/dist/types/paginatedEventsTypes.cjs +19 -0
  75. package/dist/types/paginatedEventsTypes.cjs.map +1 -0
  76. package/dist/types/paginatedEventsTypes.d.cts +115 -0
  77. package/dist/types/paginatedEventsTypes.d.ts +115 -0
  78. package/dist/types/paginatedEventsTypes.js +1 -0
  79. package/dist/types/paginatedEventsTypes.js.map +1 -0
  80. package/dist/types/serverModelTypes.cjs +19 -0
  81. package/dist/types/serverModelTypes.cjs.map +1 -0
  82. package/dist/types/serverModelTypes.d.cts +224 -0
  83. package/dist/types/serverModelTypes.d.ts +224 -0
  84. package/dist/types/serverModelTypes.js +1 -0
  85. package/dist/types/serverModelTypes.js.map +1 -0
  86. package/dist/types/serverTypes.cjs +19 -0
  87. package/dist/types/serverTypes.cjs.map +1 -0
  88. package/dist/types/serverTypes.d.cts +304 -0
  89. package/dist/types/serverTypes.d.ts +304 -0
  90. package/dist/types/serverTypes.js +1 -0
  91. package/dist/types/serverTypes.js.map +1 -0
  92. package/dist/utils/index.cjs +25 -0
  93. package/dist/utils/index.cjs.map +1 -0
  94. package/dist/utils/index.d.cts +12 -0
  95. package/dist/utils/index.d.ts +12 -0
  96. package/dist/utils/index.js +3 -0
  97. package/dist/utils/index.js.map +1 -0
  98. package/dist/utils/indexerBalanceValue.cjs +43 -0
  99. package/dist/utils/indexerBalanceValue.cjs.map +1 -0
  100. package/dist/utils/indexerBalanceValue.d.cts +39 -0
  101. package/dist/utils/indexerBalanceValue.d.ts +39 -0
  102. package/dist/utils/indexerBalanceValue.js +16 -0
  103. package/dist/utils/indexerBalanceValue.js.map +1 -0
  104. package/package.json +53 -0
  105. package/src/IndexerBaseClient.ts +851 -0
  106. package/src/IndexerClient.ts +468 -0
  107. package/src/dataMappers.ts +362 -0
  108. package/src/endpoints.ts +7 -0
  109. package/src/index.ts +4 -0
  110. package/src/types/CandlestickPeriod.ts +11 -0
  111. package/src/types/IndexerEventType.ts +9 -0
  112. package/src/types/IndexerLeaderboardType.ts +2 -0
  113. package/src/types/NadoTx.ts +63 -0
  114. package/src/types/clientTypes.ts +679 -0
  115. package/src/types/collateralEventType.ts +4 -0
  116. package/src/types/index.ts +9 -0
  117. package/src/types/paginatedEventsTypes.ts +194 -0
  118. package/src/types/serverModelTypes.ts +271 -0
  119. package/src/types/serverTypes.ts +427 -0
  120. package/src/utils/index.ts +1 -0
  121. package/src/utils/indexerBalanceValue.ts +46 -0
@@ -0,0 +1,851 @@
1
+ import {
2
+ EIP712LeaderboardAuthenticationParams,
3
+ EIP712LeaderboardAuthenticationValues,
4
+ getDefaultRecvTime,
5
+ getNadoEIP712Values,
6
+ getSignedTransactionRequest,
7
+ SignableRequestType,
8
+ SignableRequestTypeToParams,
9
+ SignedTx,
10
+ subaccountFromHex,
11
+ subaccountToHex,
12
+ WalletClientWithAccount,
13
+ } from '@nadohq/contracts';
14
+ import {
15
+ getValidatedHex,
16
+ mapValues,
17
+ nowInSeconds,
18
+ removeDecimals,
19
+ toBigDecimal,
20
+ toBigInt,
21
+ toIntegerString,
22
+ WalletNotProvidedError,
23
+ } from '@nadohq/utils';
24
+ import axios, { AxiosInstance, AxiosResponse } from 'axios';
25
+ import {
26
+ mapIndexerCandlesticks,
27
+ mapIndexerEvent,
28
+ mapIndexerEventWithTx,
29
+ mapIndexerFundingRate,
30
+ mapIndexerLeaderboardContest,
31
+ mapIndexerLeaderboardPosition,
32
+ mapIndexerLeaderboardRegistration,
33
+ mapIndexerMakerStatistics,
34
+ mapIndexerMarketSnapshot,
35
+ mapIndexerMatchEventBalances,
36
+ mapIndexerOrder,
37
+ mapIndexerPerpPrices,
38
+ mapIndexerProductPayment,
39
+ mapIndexerServerProduct,
40
+ mapIndexerVlpSnapshot,
41
+ mapSnapshotsIntervalToServerParams,
42
+ } from './dataMappers';
43
+ import {
44
+ GetIndexerBacklogResponse,
45
+ GetIndexerCandlesticksParams,
46
+ GetIndexerCandlesticksResponse,
47
+ GetIndexerEdgeCandlesticksParams,
48
+ GetIndexerEdgeCandlesticksResponse,
49
+ GetIndexerEdgeMarketSnapshotResponse,
50
+ GetIndexerEdgeMarketSnapshotsParams,
51
+ GetIndexerEventsParams,
52
+ GetIndexerEventsResponse,
53
+ GetIndexerFastWithdrawalSignatureParams,
54
+ GetIndexerFastWithdrawalSignatureResponse,
55
+ GetIndexerFundingRateParams,
56
+ GetIndexerFundingRateResponse,
57
+ GetIndexerInterestFundingPaymentsParams,
58
+ GetIndexerInterestFundingPaymentsResponse,
59
+ GetIndexerLeaderboardContestsParams,
60
+ GetIndexerLeaderboardContestsResponse,
61
+ GetIndexerLeaderboardParams,
62
+ GetIndexerLeaderboardParticipantParams,
63
+ GetIndexerLeaderboardParticipantResponse,
64
+ GetIndexerLeaderboardRegistrationParams,
65
+ GetIndexerLeaderboardRegistrationResponse,
66
+ GetIndexerLeaderboardResponse,
67
+ GetIndexerLinkedSignerParams,
68
+ GetIndexerLinkedSignerResponse,
69
+ GetIndexerMakerStatisticsParams,
70
+ GetIndexerMakerStatisticsResponse,
71
+ GetIndexerMarketSnapshotsParams,
72
+ GetIndexerMarketSnapshotsResponse,
73
+ GetIndexerMatchEventsParams,
74
+ GetIndexerMatchEventsResponse,
75
+ GetIndexerMultiProductFundingRatesParams,
76
+ GetIndexerMultiProductFundingRatesResponse,
77
+ GetIndexerMultiProductPerpPricesParams,
78
+ GetIndexerMultiProductPerpPricesResponse,
79
+ GetIndexerMultiProductSnapshotsParams,
80
+ GetIndexerMultiProductSnapshotsResponse,
81
+ GetIndexerMultiSubaccountSnapshotsParams,
82
+ GetIndexerMultiSubaccountSnapshotsResponse,
83
+ GetIndexerOraclePricesParams,
84
+ GetIndexerOraclePricesResponse,
85
+ GetIndexerOrdersParams,
86
+ GetIndexerOrdersResponse,
87
+ GetIndexerPerpPricesParams,
88
+ GetIndexerPerpPricesResponse,
89
+ GetIndexerProductSnapshotsParams,
90
+ GetIndexerProductSnapshotsResponse,
91
+ GetIndexerQuotePriceResponse,
92
+ GetIndexerReferralCodeParams,
93
+ GetIndexerReferralCodeResponse,
94
+ GetIndexerVlpSnapshotsParams,
95
+ GetIndexerVlpSnapshotsResponse,
96
+ IndexerEventWithTx,
97
+ IndexerMatchEvent,
98
+ IndexerOraclePrice,
99
+ IndexerServerEventsParams,
100
+ IndexerServerQueryRequestByType,
101
+ IndexerServerQueryRequestType,
102
+ IndexerServerQueryResponseByType,
103
+ IndexerSnapshotBalance,
104
+ IndexerSubaccountSnapshot,
105
+ ListIndexerSubaccountsParams,
106
+ ListIndexerSubaccountsResponse,
107
+ UpdateIndexerLeaderboardRegistrationParams,
108
+ UpdateIndexerLeaderboardRegistrationResponse,
109
+ } from './types';
110
+
111
+ export interface IndexerClientOpts {
112
+ // Server URLs
113
+ url: string;
114
+ v2Url?: string;
115
+ // Wallet Client for EIP712 signing
116
+ walletClient?: WalletClientWithAccount;
117
+ }
118
+
119
+ type IndexerQueryRequestBody = Partial<IndexerServerQueryRequestByType>;
120
+
121
+ /**
122
+ * Base client for all indexer requests
123
+ */
124
+ export class IndexerBaseClient {
125
+ readonly opts: IndexerClientOpts;
126
+ readonly v2Url: string;
127
+ readonly axiosInstance: AxiosInstance;
128
+
129
+ constructor(opts: IndexerClientOpts) {
130
+ this.opts = opts;
131
+ this.axiosInstance = axios.create({
132
+ withCredentials: true,
133
+ });
134
+ this.v2Url = opts.v2Url ? opts.v2Url : opts.url.replace('v1', 'v2');
135
+ }
136
+
137
+ /**
138
+ * List all subaccounts
139
+ *
140
+ * @param params
141
+ */
142
+ async listSubaccounts(
143
+ params: ListIndexerSubaccountsParams,
144
+ ): Promise<ListIndexerSubaccountsResponse> {
145
+ const baseResponse = await this.query('subaccounts', params);
146
+
147
+ return baseResponse.subaccounts.map((item) => {
148
+ const subaccount = subaccountFromHex(item.subaccount);
149
+ return {
150
+ hexId: item.subaccount,
151
+ ...subaccount,
152
+ };
153
+ });
154
+ }
155
+
156
+ /**
157
+ * Retrieve snapshots of multiple subaccounts at multiple points in time.
158
+ * Each snapshot is a view of the subaccount's balances at this point in time, with tracked variables for interest, funding, etc.
159
+ *
160
+ * @param params
161
+ */
162
+ async getMultiSubaccountSnapshots(
163
+ params: GetIndexerMultiSubaccountSnapshotsParams,
164
+ ): Promise<GetIndexerMultiSubaccountSnapshotsResponse> {
165
+ const subaccountHexIds = params.subaccounts.map(
166
+ ({ subaccountOwner, subaccountName }) =>
167
+ subaccountToHex({
168
+ subaccountOwner,
169
+ subaccountName,
170
+ }),
171
+ );
172
+
173
+ const baseResponse = await this.query('account_snapshots', {
174
+ subaccounts: subaccountHexIds,
175
+ timestamps: params.timestamps,
176
+ isolated: params.isolated,
177
+ });
178
+
179
+ const snapshotsBySubaccount = mapValues(
180
+ baseResponse.snapshots,
181
+ (balanceSnapshots) => {
182
+ const snapshotByTimestamp: Record<string, IndexerSubaccountSnapshot> =
183
+ {};
184
+
185
+ Object.entries(balanceSnapshots).forEach(([timestamp, events]) => {
186
+ const balances: IndexerSnapshotBalance[] =
187
+ events.map(mapIndexerEvent);
188
+
189
+ snapshotByTimestamp[timestamp] = {
190
+ timestamp: toBigDecimal(timestamp),
191
+ balances,
192
+ };
193
+ });
194
+
195
+ return snapshotByTimestamp;
196
+ },
197
+ );
198
+
199
+ return {
200
+ subaccountHexIds,
201
+ snapshots: snapshotsBySubaccount,
202
+ };
203
+ }
204
+
205
+ /**
206
+ * Retrieves referral code for an address
207
+ *
208
+ * @param params
209
+ */
210
+ async getReferralCode(
211
+ params: GetIndexerReferralCodeParams,
212
+ ): Promise<GetIndexerReferralCodeResponse> {
213
+ const baseResponse = await this.query('referral_code', {
214
+ subaccount: subaccountToHex({
215
+ subaccountOwner: params.subaccount.subaccountOwner,
216
+ subaccountName: params.subaccount.subaccountName,
217
+ }),
218
+ });
219
+
220
+ return {
221
+ referralCode: baseResponse.referral_code,
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Retrieves funding rate for a product, where 1 = 100%
227
+ * @param params
228
+ */
229
+ async getFundingRate(
230
+ params: GetIndexerFundingRateParams,
231
+ ): Promise<GetIndexerFundingRateResponse> {
232
+ const baseResponse = await this.query('funding_rate', {
233
+ product_id: params.productId,
234
+ });
235
+
236
+ return mapIndexerFundingRate(baseResponse);
237
+ }
238
+
239
+ /**
240
+ * Retrieves funding rate for multiple products, where 1 = 100%
241
+ * @param params
242
+ */
243
+ async getMultiProductFundingRates(
244
+ params: GetIndexerMultiProductFundingRatesParams,
245
+ ): Promise<GetIndexerMultiProductFundingRatesResponse> {
246
+ const baseResponse = await this.query('funding_rates', {
247
+ product_ids: params.productIds,
248
+ });
249
+
250
+ return mapValues(baseResponse, mapIndexerFundingRate);
251
+ }
252
+
253
+ /**
254
+ * Retrieves latest mark/index price for a perp product
255
+ * @param params
256
+ */
257
+ async getPerpPrices(
258
+ params: GetIndexerPerpPricesParams,
259
+ ): Promise<GetIndexerPerpPricesResponse> {
260
+ const baseResponse = await this.query('price', {
261
+ product_id: params.productId,
262
+ });
263
+
264
+ return mapIndexerPerpPrices(baseResponse);
265
+ }
266
+
267
+ /**
268
+ * Retrieves latest mark/index price for multiple perp products
269
+ * @param params
270
+ */
271
+ async getMultiProductPerpPrices(
272
+ params: GetIndexerMultiProductPerpPricesParams,
273
+ ): Promise<GetIndexerMultiProductPerpPricesResponse> {
274
+ const baseResponse = await this.query('perp_prices', {
275
+ product_ids: params.productIds,
276
+ });
277
+
278
+ return mapValues(baseResponse, mapIndexerPerpPrices);
279
+ }
280
+
281
+ /**
282
+ * Retrieves latest oracle prices for provided products
283
+ * @param params
284
+ */
285
+ async getOraclePrices(
286
+ params: GetIndexerOraclePricesParams,
287
+ ): Promise<GetIndexerOraclePricesResponse> {
288
+ const baseResponse = await this.query('oracle_price', {
289
+ product_ids: params.productIds,
290
+ });
291
+
292
+ return baseResponse.prices.map((price): IndexerOraclePrice => {
293
+ return {
294
+ oraclePrice: removeDecimals(price.oracle_price_x18),
295
+ updateTime: toBigDecimal(price.update_time),
296
+ productId: price.product_id,
297
+ };
298
+ });
299
+ }
300
+
301
+ /**
302
+ * Retrieves candlesticks for a product
303
+ * @param params
304
+ */
305
+ async getCandlesticks(
306
+ params: GetIndexerCandlesticksParams,
307
+ ): Promise<GetIndexerCandlesticksResponse> {
308
+ const baseResponse = await this.query('candlesticks', {
309
+ product_id: params.productId,
310
+ max_time: params.maxTimeInclusive,
311
+ limit: params.limit,
312
+ granularity: params.period,
313
+ });
314
+
315
+ return baseResponse.candlesticks.map(mapIndexerCandlesticks);
316
+ }
317
+
318
+ /**
319
+ * Retrieves candlesticks for a product from Edge
320
+ * @param params
321
+ */
322
+ async getEdgeCandlesticks(
323
+ params: GetIndexerEdgeCandlesticksParams,
324
+ ): Promise<GetIndexerEdgeCandlesticksResponse> {
325
+ const baseResponse = await this.query('edge_candlesticks', {
326
+ product_id: params.productId,
327
+ max_time: params.maxTimeInclusive,
328
+ limit: params.limit,
329
+ granularity: params.period,
330
+ });
331
+
332
+ return baseResponse.candlesticks.map(mapIndexerCandlesticks);
333
+ }
334
+
335
+ /**
336
+ * Retrieves historical snapshots for a product
337
+ * @param params
338
+ */
339
+ async getProductSnapshots(
340
+ params: GetIndexerProductSnapshotsParams,
341
+ ): Promise<GetIndexerProductSnapshotsResponse> {
342
+ const baseResponse = await this.query('products', {
343
+ product_id: params.productId,
344
+ max_time: params.maxTimestampInclusive,
345
+ limit: params.limit,
346
+ idx: params.startCursor,
347
+ });
348
+
349
+ return baseResponse.products.map((product) => {
350
+ return {
351
+ ...mapIndexerServerProduct(product.product),
352
+ submissionIndex: product.submission_idx,
353
+ };
354
+ });
355
+ }
356
+
357
+ /**
358
+ * Retrieves historical snapshots for multiple products
359
+ * @param params
360
+ */
361
+ async getMultiProductSnapshots(
362
+ params: GetIndexerMultiProductSnapshotsParams,
363
+ ): Promise<GetIndexerMultiProductSnapshotsResponse> {
364
+ const timestampToProductsMap = await this.query('product_snapshots', {
365
+ product_ids: params.productIds,
366
+ max_time: params.maxTimestampInclusive ?? [nowInSeconds()],
367
+ });
368
+
369
+ return mapValues(timestampToProductsMap, (productIdToProduct) => {
370
+ return mapValues(productIdToProduct, (indexerProduct) => {
371
+ return {
372
+ ...mapIndexerServerProduct(indexerProduct.product),
373
+ submissionIndex: indexerProduct.submission_idx,
374
+ };
375
+ });
376
+ });
377
+ }
378
+
379
+ /**
380
+ * Retrieves historical events
381
+ *
382
+ * @param params
383
+ */
384
+ async getEvents(
385
+ params: GetIndexerEventsParams,
386
+ ): Promise<GetIndexerEventsResponse> {
387
+ const serverLimit = ((): IndexerServerEventsParams['limit'] | undefined => {
388
+ if (!params.limit) {
389
+ return;
390
+ }
391
+
392
+ if (params.limit.type === 'events') {
393
+ return {
394
+ raw: params.limit.value,
395
+ };
396
+ }
397
+ return {
398
+ txs: params.limit.value,
399
+ };
400
+ })();
401
+
402
+ const baseResponse = await this.query('events', {
403
+ subaccount: params.subaccount
404
+ ? subaccountToHex({
405
+ subaccountOwner: params.subaccount.subaccountOwner,
406
+ subaccountName: params.subaccount.subaccountName,
407
+ })
408
+ : undefined,
409
+ product_ids: params.productIds,
410
+ isolated: params.isolated,
411
+ event_types: params.eventTypes,
412
+ max_time: params.maxTimestampInclusive,
413
+ desc: params.desc,
414
+ limit: serverLimit,
415
+ idx: params.startCursor,
416
+ });
417
+
418
+ // Keep track of the last tx index, and go to the next one if the submission_idx for the currently processed event does not match
419
+ // txs are ordered the same as events, so this should be correct
420
+ let lastTxIdx = 0;
421
+ return baseResponse.events.map((event): IndexerEventWithTx => {
422
+ if (baseResponse.txs[lastTxIdx].submission_idx !== event.submission_idx) {
423
+ lastTxIdx += 1;
424
+ }
425
+ const tx = baseResponse.txs[lastTxIdx];
426
+ return mapIndexerEventWithTx(event, tx);
427
+ });
428
+ }
429
+
430
+ /**
431
+ * Retrieves historical orders
432
+ * @param params
433
+ */
434
+ async getOrders(
435
+ params: GetIndexerOrdersParams,
436
+ ): Promise<GetIndexerOrdersResponse> {
437
+ const baseResponse = await this.query('orders', {
438
+ subaccount: params.subaccount
439
+ ? subaccountToHex({
440
+ subaccountOwner: params.subaccount.subaccountOwner,
441
+ subaccountName: params.subaccount.subaccountName,
442
+ })
443
+ : undefined,
444
+ product_ids: params.productIds,
445
+ isolated: params.isolated,
446
+ digests: params.digests,
447
+ max_time: params.maxTimestampInclusive,
448
+ limit: params.limit,
449
+ idx: params.startCursor,
450
+ });
451
+
452
+ return baseResponse.orders.map(mapIndexerOrder);
453
+ }
454
+
455
+ /**
456
+ * Gets match order events, this will return the same events as the events query, but with additional information
457
+ * to identify the order that was matched
458
+ *
459
+ * @param params
460
+ */
461
+ async getMatchEvents(
462
+ params: GetIndexerMatchEventsParams,
463
+ ): Promise<GetIndexerMatchEventsResponse> {
464
+ const baseResponse = await this.query('matches', {
465
+ subaccount: params.subaccount
466
+ ? subaccountToHex({
467
+ subaccountOwner: params.subaccount.subaccountOwner,
468
+ subaccountName: params.subaccount.subaccountName,
469
+ })
470
+ : undefined,
471
+ product_ids: params.productIds,
472
+ isolated: params.isolated,
473
+ max_time: params.maxTimestampInclusive,
474
+ limit: params.limit,
475
+ idx: params.startCursor,
476
+ });
477
+
478
+ // Same as logic in `getEvents`
479
+ let lastTxIdx = 0;
480
+ return baseResponse.matches.map((matchEvent): IndexerMatchEvent => {
481
+ if (
482
+ baseResponse.txs[lastTxIdx].submission_idx !== matchEvent.submission_idx
483
+ ) {
484
+ lastTxIdx += 1;
485
+ }
486
+ const { tx, timestamp } = baseResponse.txs[lastTxIdx];
487
+
488
+ // We use this to derive the product ID for the match
489
+ const postBalances = mapIndexerMatchEventBalances(
490
+ matchEvent.post_balance,
491
+ );
492
+
493
+ return {
494
+ productId: postBalances.base.productId,
495
+ isolated: matchEvent.isolated,
496
+ totalFee: toBigDecimal(matchEvent.fee),
497
+ sequencerFee: toBigDecimal(matchEvent.sequencer_fee),
498
+ baseFilled: toBigDecimal(matchEvent.base_filled),
499
+ quoteFilled: toBigDecimal(matchEvent.quote_filled),
500
+ cumulativeFee: toBigDecimal(matchEvent.cumulative_fee),
501
+ cumulativeBaseFilled: toBigDecimal(matchEvent.cumulative_base_filled),
502
+ cumulativeQuoteFilled: toBigDecimal(matchEvent.cumulative_quote_filled),
503
+ digest: matchEvent.digest,
504
+ order: matchEvent.order,
505
+ submissionIndex: matchEvent.submission_idx,
506
+ timestamp: toBigDecimal(timestamp),
507
+ preEventTrackedVars: {
508
+ netEntryUnrealized: toBigDecimal(matchEvent.net_entry_unrealized),
509
+ netEntryCumulative: toBigDecimal(matchEvent.net_entry_cumulative),
510
+ },
511
+ preBalances: mapIndexerMatchEventBalances(matchEvent.pre_balance),
512
+ postBalances,
513
+ tx,
514
+ ...subaccountFromHex(matchEvent.order.sender),
515
+ };
516
+ });
517
+ }
518
+
519
+ /**
520
+ * Retrieves historical funding & interest payments.
521
+ * NOTE: `limit` is an upperbound. If a user changes position size such that his position is 0 during each funding/interest tick,
522
+ * then the indexer will return fewer than `limit` results per page. However, more events can be present. This means that
523
+ * there isn't a reliable way to determine whether there is a next page. We just need to keep paginating until the next cursor is null.
524
+ *
525
+ * @param params
526
+ */
527
+ async getInterestFundingPayments(
528
+ params: GetIndexerInterestFundingPaymentsParams,
529
+ ): Promise<GetIndexerInterestFundingPaymentsResponse> {
530
+ const baseResponse = await this.query('interest_and_funding', {
531
+ subaccount: subaccountToHex({
532
+ subaccountOwner: params.subaccount.subaccountOwner,
533
+ subaccountName: params.subaccount.subaccountName,
534
+ }),
535
+ product_ids: params.productIds,
536
+ max_time: params.maxTimestampInclusive,
537
+ limit: params.limit,
538
+ max_idx: params.startCursor,
539
+ });
540
+
541
+ return {
542
+ fundingPayments: baseResponse.funding_payments.map(
543
+ mapIndexerProductPayment,
544
+ ),
545
+ interestPayments: baseResponse.interest_payments.map(
546
+ mapIndexerProductPayment,
547
+ ),
548
+ nextCursor: baseResponse.next_idx,
549
+ };
550
+ }
551
+
552
+ /**
553
+ * Gets quote (USDC) price in terms of USD
554
+ */
555
+ async getQuotePrice(): Promise<GetIndexerQuotePriceResponse> {
556
+ const baseResponse = await this.query('usdc_price', {});
557
+ return {
558
+ price: removeDecimals(baseResponse.price_x18),
559
+ };
560
+ }
561
+
562
+ /**
563
+ * Fetches currently registered linked signer with the remaining txs allowed for the subaccount
564
+ */
565
+ async getLinkedSignerWithRateLimit(
566
+ params: GetIndexerLinkedSignerParams,
567
+ ): Promise<GetIndexerLinkedSignerResponse> {
568
+ const baseResponse = await this.query('linked_signer_rate_limit', {
569
+ subaccount: subaccountToHex(params.subaccount),
570
+ });
571
+ return {
572
+ totalTxLimit: toBigDecimal(baseResponse.total_tx_limit),
573
+ remainingTxs: toBigDecimal(baseResponse.remaining_tx),
574
+ signer: baseResponse.signer,
575
+ waitTimeUntilNextTx: toBigDecimal(baseResponse.wait_time),
576
+ };
577
+ }
578
+
579
+ /**
580
+ * Retrieve historical market snapshots
581
+ * @param params
582
+ */
583
+ async getMarketSnapshots(
584
+ params: GetIndexerMarketSnapshotsParams,
585
+ ): Promise<GetIndexerMarketSnapshotsResponse> {
586
+ const baseResponse = await this.query('market_snapshots', {
587
+ interval: mapSnapshotsIntervalToServerParams(params),
588
+ product_ids: params.productIds,
589
+ });
590
+
591
+ return baseResponse.snapshots.map(mapIndexerMarketSnapshot);
592
+ }
593
+
594
+ /**
595
+ * Retrieve historical market snapshots from Edge
596
+ * @param params
597
+ */
598
+ async getEdgeMarketSnapshots(
599
+ params: GetIndexerEdgeMarketSnapshotsParams,
600
+ ): Promise<GetIndexerEdgeMarketSnapshotResponse> {
601
+ const baseResponse = await this.query('edge_market_snapshots', {
602
+ interval: mapSnapshotsIntervalToServerParams(params),
603
+ });
604
+
605
+ return mapValues(baseResponse.snapshots, (snapshots) =>
606
+ snapshots.map(mapIndexerMarketSnapshot),
607
+ );
608
+ }
609
+
610
+ /**
611
+ * Retrieve maker statistics for a given epoch
612
+ *
613
+ * @param params
614
+ */
615
+ async getMakerStatistics(
616
+ params: GetIndexerMakerStatisticsParams,
617
+ ): Promise<GetIndexerMakerStatisticsResponse> {
618
+ const baseResponse = await this.query('maker_statistics', {
619
+ product_id: params.productId,
620
+ epoch: params.epoch,
621
+ interval: params.interval,
622
+ });
623
+
624
+ return {
625
+ rewardCoefficient: toBigDecimal(baseResponse.reward_coefficient),
626
+ makers: baseResponse.makers.map(mapIndexerMakerStatistics),
627
+ };
628
+ }
629
+
630
+ /**
631
+ * Retrieve leaderboard stats for a given contest
632
+ *
633
+ * @param params
634
+ */
635
+ async getLeaderboard(
636
+ params: GetIndexerLeaderboardParams,
637
+ ): Promise<GetIndexerLeaderboardResponse> {
638
+ const baseResponse = await this.query('leaderboard', {
639
+ contest_id: params.contestId,
640
+ rank_type: params.rankType,
641
+ start: params.startCursor,
642
+ limit: params.limit,
643
+ });
644
+
645
+ return {
646
+ participants: baseResponse.positions.map(mapIndexerLeaderboardPosition),
647
+ };
648
+ }
649
+
650
+ /**
651
+ * Retrieve leaderboard ranking of a subaccount on a given contest
652
+ *
653
+ * @param params
654
+ */
655
+ async getLeaderboardParticipant(
656
+ params: GetIndexerLeaderboardParticipantParams,
657
+ ): Promise<GetIndexerLeaderboardParticipantResponse> {
658
+ const baseResponse = await this.query('leaderboard_rank', {
659
+ subaccount: subaccountToHex(params.subaccount),
660
+ contest_ids: params.contestIds,
661
+ });
662
+
663
+ return {
664
+ participant: mapValues(baseResponse.positions, (position) =>
665
+ mapIndexerLeaderboardPosition(position),
666
+ ),
667
+ };
668
+ }
669
+
670
+ /**
671
+ * Attempts to update a user's registration to the provided `contestId`. This requires signing.
672
+ * @param params
673
+ */
674
+ async updateLeaderboardRegistration(
675
+ params: UpdateIndexerLeaderboardRegistrationParams,
676
+ ): Promise<UpdateIndexerLeaderboardRegistrationResponse> {
677
+ const signatureParams: EIP712LeaderboardAuthenticationParams = {
678
+ // Default to 90 seconds from now if no recvTime is provided
679
+ expiration: toIntegerString(params.recvTime ?? getDefaultRecvTime()),
680
+ subaccountName: params.subaccountName,
681
+ subaccountOwner: params.subaccountOwner,
682
+ };
683
+
684
+ const tx = getNadoEIP712Values(
685
+ 'leaderboard_authentication',
686
+ signatureParams,
687
+ );
688
+ const signature = await this.sign(
689
+ 'leaderboard_authentication',
690
+ params.updateRegistration.verifyingAddr,
691
+ params.updateRegistration.chainId,
692
+ signatureParams,
693
+ );
694
+
695
+ const updateRegistrationTx: SignedTx<EIP712LeaderboardAuthenticationValues> =
696
+ {
697
+ tx,
698
+ signature,
699
+ };
700
+
701
+ const baseResponse = await this.query('leaderboard_registration', {
702
+ subaccount: subaccountToHex({
703
+ subaccountOwner: params.subaccountOwner,
704
+ subaccountName: params.subaccountName,
705
+ }),
706
+ contest_id: params.contestId,
707
+ update_registration: updateRegistrationTx,
708
+ });
709
+ return {
710
+ registration: baseResponse.registration
711
+ ? mapIndexerLeaderboardRegistration(baseResponse.registration)
712
+ : null,
713
+ };
714
+ }
715
+
716
+ /**
717
+ * Retrieves the registration status for a leaderboard participant for provided `contestId`.
718
+ * @param params
719
+ */
720
+ async getLeaderboardRegistration(
721
+ params: GetIndexerLeaderboardRegistrationParams,
722
+ ): Promise<GetIndexerLeaderboardRegistrationResponse> {
723
+ const baseResponse = await this.query('leaderboard_registration', {
724
+ subaccount: subaccountToHex({
725
+ subaccountOwner: params.subaccountOwner,
726
+ subaccountName: params.subaccountName,
727
+ }),
728
+ contest_id: params.contestId,
729
+ update_registration: null,
730
+ });
731
+ return {
732
+ registration: baseResponse.registration
733
+ ? mapIndexerLeaderboardRegistration(baseResponse.registration)
734
+ : null,
735
+ };
736
+ }
737
+
738
+ /**
739
+ * Retrieve metadata of provided leaderboard contests
740
+ *
741
+ * @param params
742
+ */
743
+ async getLeaderboardContests(
744
+ params: GetIndexerLeaderboardContestsParams,
745
+ ): Promise<GetIndexerLeaderboardContestsResponse> {
746
+ const baseResponse = await this.query('leaderboard_contests', {
747
+ contest_ids: params.contestIds,
748
+ });
749
+
750
+ return {
751
+ contests: baseResponse.contests.map(mapIndexerLeaderboardContest),
752
+ };
753
+ }
754
+
755
+ /**
756
+ * Retrieve signature and tx to submit a fast withdrawal
757
+ *
758
+ * @param params
759
+ */
760
+ async getFastWithdrawalSignature(
761
+ params: GetIndexerFastWithdrawalSignatureParams,
762
+ ): Promise<GetIndexerFastWithdrawalSignatureResponse> {
763
+ const baseResponse = await this.query('fast_withdrawal_signature', params);
764
+ return {
765
+ idx: toBigInt(baseResponse.idx),
766
+ tx: baseResponse.tx,
767
+ txBytes: getValidatedHex(baseResponse.tx_bytes),
768
+ signatures: baseResponse.signatures.map(getValidatedHex),
769
+ };
770
+ }
771
+
772
+ async getVlpSnapshots(
773
+ params: GetIndexerVlpSnapshotsParams,
774
+ ): Promise<GetIndexerVlpSnapshotsResponse> {
775
+ const baseResponse = await this.query('vlp_snapshots', {
776
+ interval: {
777
+ count: params.limit,
778
+ max_time: params.maxTimeInclusive
779
+ ? toIntegerString(params.maxTimeInclusive)
780
+ : undefined,
781
+ granularity: params.granularity,
782
+ },
783
+ });
784
+
785
+ return {
786
+ snapshots: baseResponse.snapshots.map(mapIndexerVlpSnapshot),
787
+ };
788
+ }
789
+
790
+ async getSequencerBacklog(): Promise<GetIndexerBacklogResponse> {
791
+ const baseResponse = await this.query('backlog', {});
792
+
793
+ return {
794
+ totalTxs: toBigDecimal(baseResponse.total_txs),
795
+ totalSubmissions: toBigDecimal(baseResponse.total_submissions),
796
+ backlogSize: toBigDecimal(baseResponse.backlog_size),
797
+ updatedAt: toBigDecimal(baseResponse.updated_at),
798
+ backlogEtaInSeconds: baseResponse.backlog_eta_in_seconds
799
+ ? toBigDecimal(baseResponse.backlog_eta_in_seconds)
800
+ : null,
801
+ txsPerSecond: baseResponse.txs_per_second
802
+ ? toBigDecimal(baseResponse.txs_per_second)
803
+ : null,
804
+ };
805
+ }
806
+
807
+ protected async query<TRequestType extends IndexerServerQueryRequestType>(
808
+ requestType: TRequestType,
809
+ params: IndexerServerQueryRequestByType[TRequestType],
810
+ ): Promise<IndexerServerQueryResponseByType[TRequestType]> {
811
+ const reqBody: IndexerQueryRequestBody = {
812
+ [requestType]: params,
813
+ };
814
+ const response = await this.axiosInstance.post<
815
+ IndexerServerQueryResponseByType[TRequestType]
816
+ >(this.opts.url, reqBody);
817
+
818
+ this.checkResponseStatus(response);
819
+
820
+ return response.data;
821
+ }
822
+
823
+ protected async sign<T extends SignableRequestType>(
824
+ requestType: T,
825
+ verifyingContract: string,
826
+ chainId: number,
827
+ params: SignableRequestTypeToParams[T],
828
+ ) {
829
+ const walletClient = this.opts.walletClient;
830
+
831
+ if (!walletClient) {
832
+ throw new WalletNotProvidedError();
833
+ }
834
+
835
+ return getSignedTransactionRequest({
836
+ chainId,
837
+ requestParams: params,
838
+ requestType,
839
+ walletClient,
840
+ verifyingContract,
841
+ });
842
+ }
843
+
844
+ private checkResponseStatus(response: AxiosResponse) {
845
+ if (response.status !== 200 || !response.data) {
846
+ throw Error(
847
+ `Unexpected response from server: ${response.status} ${response.statusText}`,
848
+ );
849
+ }
850
+ }
851
+ }