@fleet-sdk/blockchain-providers 0.6.1 → 0.6.3

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.
@@ -58,6 +58,8 @@ import {
58
58
  UNCONF_TX_QUERY
59
59
  } from "./queries";
60
60
 
61
+ type SkipAndTake = { skip?: number; take?: number };
62
+
61
63
  export type GraphQLBoxWhere = BoxWhere & {
62
64
  /** Base16-encoded ErgoTrees */
63
65
  ergoTrees?: HexString[];
@@ -152,15 +154,18 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
152
154
  return this as unknown as ErgoGraphQLProvider<M>;
153
155
  }
154
156
 
155
- async *streamBoxes(query: GraphQLBoxQuery): AsyncGenerator<ChainProviderBox<I>[]> {
157
+ async *streamBoxes(
158
+ query: GraphQLBoxQuery & SkipAndTake
159
+ ): AsyncGenerator<ChainProviderBox<I>[]> {
156
160
  if (isEmpty(query.where)) {
157
161
  throw new Error("Cannot fetch unspent boxes without a where clause.");
158
162
  }
159
163
 
160
164
  const notBeingSpent = (box: GQLBox) => !box.beingSpent;
161
165
  const returnedBoxIds = new Set<string>();
162
- const { where, from } = query;
163
- const queries = buildGqlBoxQueries(where);
166
+ const { from, take } = query;
167
+ const pageSize = take ?? PAGE_SIZE;
168
+ const queries = buildGqlBoxQueries(query);
164
169
  const isMempoolAware = from !== "blockchain";
165
170
 
166
171
  for (const query of queries) {
@@ -180,7 +185,7 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
180
185
  boxes = boxes.concat(confirmedBoxes);
181
186
  }
182
187
 
183
- inclChain = data.boxes.length === PAGE_SIZE;
188
+ inclChain = data.boxes.length === pageSize;
184
189
  }
185
190
 
186
191
  if (isMempoolAware && hasMempool(data)) {
@@ -191,7 +196,7 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
191
196
  boxes = boxes.concat(mempoolBoxes);
192
197
  }
193
198
 
194
- inclPool = data.mempool.boxes.length === PAGE_SIZE;
199
+ inclPool = data.mempool.boxes.length === pageSize;
195
200
  }
196
201
 
197
202
  if (some(boxes)) {
@@ -208,7 +213,7 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
208
213
  }
209
214
  }
210
215
 
211
- if (inclChain || inclPool) query.skip += PAGE_SIZE;
216
+ if (inclChain || inclPool) query.skip += pageSize;
212
217
  }
213
218
  }
214
219
  }
@@ -220,9 +225,10 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
220
225
  }
221
226
 
222
227
  async *streamUnconfirmedTransactions(
223
- query: TransactionQuery<GraphQLUnconfirmedTransactionWhere>
224
- ): AsyncIterable<ChainProviderUnconfirmedTransaction<I>[]> {
225
- const queries = buildGqlUnconfirmedTxQueries(query.where);
228
+ query: TransactionQuery<GraphQLUnconfirmedTransactionWhere> & SkipAndTake
229
+ ): AsyncGenerator<ChainProviderUnconfirmedTransaction<I>[]> {
230
+ const pageSize = query.take ?? PAGE_SIZE;
231
+ const queries = buildGqlUnconfirmedTxQueries(query);
226
232
 
227
233
  for (const query of queries) {
228
234
  let keepFetching = true;
@@ -234,8 +240,8 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
234
240
  );
235
241
  }
236
242
 
237
- keepFetching = response.data?.mempool?.transactions?.length === PAGE_SIZE;
238
- if (keepFetching) query.skip += PAGE_SIZE;
243
+ keepFetching = response.data?.mempool?.transactions?.length === pageSize;
244
+ if (keepFetching) query.skip += pageSize;
239
245
  }
240
246
  }
241
247
  }
@@ -252,9 +258,10 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
252
258
  }
253
259
 
254
260
  async *streamConfirmedTransactions(
255
- query: TransactionQuery<GraphQLConfirmedTransactionWhere>
256
- ): AsyncIterable<ChainProviderConfirmedTransaction<I>[]> {
257
- const queries = buildGqlConfirmedTxQueries(query.where);
261
+ query: TransactionQuery<GraphQLConfirmedTransactionWhere> & SkipAndTake
262
+ ): AsyncGenerator<ChainProviderConfirmedTransaction<I>[]> {
263
+ const pageSize = query.take ?? PAGE_SIZE;
264
+ const queries = buildGqlConfirmedTxQueries(query);
258
265
 
259
266
  for (const query of queries) {
260
267
  let keepFetching = true;
@@ -266,8 +273,8 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
266
273
  );
267
274
  }
268
275
 
269
- keepFetching = response.data?.transactions?.length === PAGE_SIZE;
270
- if (keepFetching) query.skip += PAGE_SIZE;
276
+ keepFetching = response.data?.transactions?.length === pageSize;
277
+ if (keepFetching) query.skip += pageSize;
271
278
  }
272
279
  }
273
280
  }
@@ -334,11 +341,11 @@ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
334
341
  }
335
342
  }
336
343
 
337
- function buildGqlBoxQueries(where: GraphQLBoxWhere) {
344
+ function buildGqlBoxQueries(query: GraphQLBoxQuery & SkipAndTake) {
338
345
  const ergoTrees = uniq(
339
346
  [
340
- merge(where.ergoTrees, where.ergoTree) ?? [],
341
- merge(where.addresses, where.address)?.map((a) =>
347
+ merge(query.where.ergoTrees, query.where.ergoTree) ?? [],
348
+ merge(query.where.addresses, query.where.address)?.map((a) =>
342
349
  typeof a === "string" ? ErgoAddress.decode(a).ergoTree : a.ergoTree
343
350
  ) ?? []
344
351
  ].flat()
@@ -346,22 +353,24 @@ function buildGqlBoxQueries(where: GraphQLBoxWhere) {
346
353
 
347
354
  return chunk(ergoTrees, MAX_ARGS).map((chunk) => ({
348
355
  spent: false,
349
- boxIds: where.boxId ? [where.boxId] : undefined,
356
+ boxIds: query.where.boxId ? [query.where.boxId] : undefined,
350
357
  ergoTrees: chunk,
351
- ergoTreeTemplateHash: where.templateHash,
352
- tokenId: where.tokenId,
353
- skip: 0,
354
- take: PAGE_SIZE
358
+ ergoTreeTemplateHash: query.where.templateHash,
359
+ tokenId: query.where.tokenId,
360
+ skip: query.skip ?? 0,
361
+ take: query.take ?? PAGE_SIZE
355
362
  }));
356
363
  }
357
364
 
358
- function buildGqlUnconfirmedTxQueries(where: GraphQLConfirmedTransactionWhere) {
365
+ function buildGqlUnconfirmedTxQueries(
366
+ query: TransactionQuery<GraphQLUnconfirmedTransactionWhere> & SkipAndTake
367
+ ) {
359
368
  const addresses = uniq(
360
369
  [
361
- merge(where.addresses, where.address)?.map((address): string =>
370
+ merge(query.where.addresses, query.where.address)?.map((address): string =>
362
371
  typeof address === "string" ? address : address.encode()
363
372
  ) ?? [],
364
- merge(where.ergoTrees, where.ergoTree)?.map((tree) =>
373
+ merge(query.where.ergoTrees, query.where.ergoTree)?.map((tree) =>
365
374
  ErgoAddress.fromErgoTree(tree).encode()
366
375
  ) ?? []
367
376
  ].flat()
@@ -369,18 +378,22 @@ function buildGqlUnconfirmedTxQueries(where: GraphQLConfirmedTransactionWhere) {
369
378
 
370
379
  return chunk(addresses, MAX_ARGS).map((chunk) => ({
371
380
  addresses: chunk.length ? chunk : undefined,
372
- transactionIds: where.transactionId ? [where.transactionId] : undefined,
373
- skip: 0,
374
- take: PAGE_SIZE
381
+ transactionIds: query.where.transactionId ? [query.where.transactionId] : undefined,
382
+ skip: query.skip ?? 0,
383
+ take: query.take ?? PAGE_SIZE
375
384
  }));
376
385
  }
377
386
 
378
- function buildGqlConfirmedTxQueries(where: GraphQLConfirmedTransactionWhere) {
379
- return buildGqlUnconfirmedTxQueries(where).map((query) => ({
380
- ...query,
381
- headerId: where.headerId,
382
- minHeight: where.minHeight,
383
- onlyRelevantOutputs: where.onlyRelevantOutputs
387
+ function buildGqlConfirmedTxQueries(
388
+ query: TransactionQuery<GraphQLConfirmedTransactionWhere> & SkipAndTake
389
+ ) {
390
+ return buildGqlUnconfirmedTxQueries(
391
+ query as TransactionQuery<GraphQLUnconfirmedTransactionWhere>
392
+ ).map((q) => ({
393
+ ...q,
394
+ headerId: query.where.headerId,
395
+ minHeight: query.where.minHeight,
396
+ onlyRelevantOutputs: query.where.onlyRelevantOutputs
384
397
  }));
385
398
  }
386
399
 
@@ -142,14 +142,14 @@ export interface IBlockchainProvider<I> {
142
142
  /**
143
143
  * Stream boxes.
144
144
  */
145
- streamBoxes(query: BoxQuery<BoxWhere>): AsyncIterable<ChainProviderBox<I>[]>;
145
+ streamBoxes(query: BoxQuery<BoxWhere>): AsyncGenerator<ChainProviderBox<I>[]>;
146
146
 
147
147
  /**
148
148
  * Stream unconfirmed transactions
149
149
  */
150
150
  streamUnconfirmedTransactions(
151
151
  query: TransactionQuery<UnconfirmedTransactionWhere>
152
- ): AsyncIterable<ChainProviderUnconfirmedTransaction<I>[]>;
152
+ ): AsyncGenerator<ChainProviderUnconfirmedTransaction<I>[]>;
153
153
 
154
154
  /**
155
155
  * Get unconfirmed transactions
@@ -163,7 +163,7 @@ export interface IBlockchainProvider<I> {
163
163
  */
164
164
  streamConfirmedTransactions(
165
165
  query: TransactionQuery<ConfirmedTransactionWhere>
166
- ): AsyncIterable<ChainProviderConfirmedTransaction<I>[]>;
166
+ ): AsyncGenerator<ChainProviderConfirmedTransaction<I>[]>;
167
167
 
168
168
  /**
169
169
  * Get confirmed transactions
@@ -1,5 +1,3 @@
1
- // export const mockSuccessResponse = (data: unknown) => resolveString(JSON.stringify(data));
2
-
3
1
  export const resolveString = (data: string) =>
4
2
  ({
5
3
  text: () => new Promise((resolve) => resolve(data))
@@ -17,6 +17,18 @@ export type FetchOptions = {
17
17
  httpOptions?: RequestInit;
18
18
  };
19
19
 
20
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
21
+ const RETRY_STATUS_CODES = new Set([
22
+ 408, // Request Timeout
23
+ 409, // Conflict
24
+ 425, // Too Early (Experimental)
25
+ 429, // Too Many Requests
26
+ 500, // Internal Server Error
27
+ 502, // Bad Gateway
28
+ 503, // Service Unavailable
29
+ 504 // Gateway Timeout
30
+ ]);
31
+
20
32
  export async function request<T>(path: string, opt?: Partial<FetchOptions>): Promise<T> {
21
33
  const url = buildURL(path, opt?.query, opt?.base);
22
34
 
@@ -24,10 +36,12 @@ export async function request<T>(path: string, opt?: Partial<FetchOptions>): Pro
24
36
  if (opt?.retry) {
25
37
  const routes = some(opt.retry.fallbacks) ? [url, ...opt.retry.fallbacks] : [url];
26
38
  const attempts = opt.retry.attempts;
27
- response = await exponentialRetry(
28
- (r) => fetch(resolveUrl(routes, attempts - r), opt.httpOptions),
29
- opt.retry
30
- );
39
+ response = await exponentialRetry(async (r) => {
40
+ const response = await fetch(resolveUrl(routes, attempts - r), opt.httpOptions);
41
+ if (RETRY_STATUS_CODES.has(response.status)) throw new Error(response.statusText);
42
+
43
+ return response;
44
+ }, opt.retry);
31
45
  } else {
32
46
  response = await fetch(url, opt?.httpOptions);
33
47
  }