@fleet-sdk/blockchain-providers 0.5.0 → 0.6.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.
@@ -1,55 +1,64 @@
1
1
  import type {
2
- Box,
2
+ Box as GQLBox,
3
3
  QueryBoxesArgs,
4
4
  Header,
5
- QueryBlockHeadersArgs
5
+ QueryBlockHeadersArgs,
6
+ Transaction,
7
+ QueryTransactionsArgs,
8
+ MempoolTransactionsArgs,
9
+ UnconfirmedTransaction,
10
+ UnconfirmedBox as GQLUnconfirmedBox
6
11
  } from "@ergo-graphql/types";
7
12
  import {
8
13
  type Base58String,
9
14
  type BlockHeader,
10
- ensureDefaults,
11
15
  type HexString,
16
+ type SignedTransaction,
17
+ ensureDefaults,
12
18
  isEmpty,
13
19
  isUndefined,
14
20
  NotSupportedError,
15
21
  orderBy,
16
- type SignedTransaction,
17
22
  some,
18
23
  uniq,
19
- uniqBy
24
+ uniqBy,
25
+ chunk
20
26
  } from "@fleet-sdk/common";
21
27
  import { ErgoAddress } from "@fleet-sdk/core";
28
+ import { hex } from "@fleet-sdk/crypto";
22
29
  import type {
23
30
  BoxQuery,
24
31
  BoxWhere,
25
32
  ChainProviderBox,
33
+ ChainProviderConfirmedTransaction,
34
+ ChainProviderUnconfirmedTransaction,
26
35
  HeaderQuery,
27
36
  IBlockchainProvider,
28
37
  TransactionEvaluationResult,
29
- TransactionReductionResult
38
+ TransactionQuery,
39
+ TransactionReductionResult,
40
+ ConfirmedTransactionWhere,
41
+ UnconfirmedTransactionWhere
30
42
  } from "../types/blockchainProvider";
31
43
  import {
32
- createGqlOperation,
33
44
  type GraphQLOperation,
34
45
  type GraphQLRequestOptions,
35
46
  type GraphQLSuccessResponse,
36
- type GraphQLThrowableOptions,
37
47
  type GraphQLVariables,
38
- isRequestParam
48
+ createGqlOperation
39
49
  } from "../utils";
40
50
  import {
41
51
  ALL_BOXES_QUERY,
42
52
  CHECK_TX_MUTATION,
43
53
  CONF_BOXES_QUERY,
54
+ CONF_TX_QUERY,
44
55
  HEADERS_QUERY,
45
56
  SEND_TX_MUTATION,
46
- UNCONF_BOXES_QUERY
57
+ UNCONF_BOXES_QUERY,
58
+ UNCONF_TX_QUERY
47
59
  } from "./queries";
48
60
 
49
61
  export type GraphQLBoxWhere = BoxWhere & {
50
- /** Base16-encoded BoxIds */
51
- boxIds?: HexString[];
52
-
53
62
  /** Base16-encoded ErgoTrees */
54
63
  ergoTrees?: HexString[];
55
64
 
@@ -57,161 +66,233 @@ export type GraphQLBoxWhere = BoxWhere & {
57
66
  addresses?: (Base58String | ErgoAddress)[];
58
67
  };
59
68
 
69
+ export type GraphQLConfirmedTransactionWhere = ConfirmedTransactionWhere & {
70
+ addresses?: (Base58String | ErgoAddress)[];
71
+ ergoTrees?: HexString[];
72
+ };
73
+
74
+ export type GraphQLUnconfirmedTransactionWhere = UnconfirmedTransactionWhere & {
75
+ addresses?: (Base58String | ErgoAddress)[];
76
+ ergoTrees?: HexString[];
77
+ };
78
+
60
79
  export type GraphQLBoxQuery = BoxQuery<GraphQLBoxWhere>;
61
80
  export type ErgoGraphQLRequestOptions = Omit<
62
81
  GraphQLRequestOptions,
63
- "throwOnNonNetworkError"
82
+ "throwOnNonNetworkErrors"
64
83
  >;
65
84
 
66
- type ConfirmedBoxesResponse = { boxes: Box[] };
67
- type UnconfirmedBoxesResponse = { mempool: { boxes: Box[] } };
85
+ type ConfirmedBoxesResponse = { boxes: GQLBox[] };
86
+ type UnconfirmedBoxesResponse = { mempool: { boxes: GQLBox[] } };
68
87
  type CombinedBoxesResponse = ConfirmedBoxesResponse & UnconfirmedBoxesResponse;
88
+ type UnconfirmedTxResponse = { mempool: { transactions: UnconfirmedTransaction[] } };
89
+ type ConfirmedTxResponse = { transactions: Transaction[] };
69
90
  type BlockHeadersResponse = { blockHeaders: Header[] };
70
91
  type CheckTransactionResponse = { checkTransaction: string };
71
92
  type TransactionSubmissionResponse = { submitTransaction: string };
72
93
  type SignedTxArgsResp = { signedTransaction: SignedTransaction };
73
94
 
74
- const PAGE_SIZE = 50;
95
+ type GraphQLThrowableOptions = ErgoGraphQLRequestOptions & {
96
+ throwOnNonNetworkErrors: true;
97
+ };
75
98
 
76
- export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
77
- #options: GraphQLThrowableOptions;
99
+ type OP<R, V extends GraphQLVariables> = GraphQLOperation<GraphQLSuccessResponse<R>, V>;
100
+ type BiMapper<T> = (value: string) => T;
78
101
 
79
- #getConfBoxes;
80
- #getUnconfBoxes;
81
- #getAllBoxes;
82
- #getHeaders;
83
- #checkTx;
84
- #sendTx;
102
+ const PAGE_SIZE = 50;
103
+ const MAX_ARGS = 20;
85
104
 
86
- constructor(url: string | URL);
87
- constructor(url: ErgoGraphQLRequestOptions);
88
- constructor(optOrUrl: ErgoGraphQLRequestOptions | string | URL) {
105
+ export class ErgoGraphQLProvider<I = bigint> implements IBlockchainProvider<I> {
106
+ #options: GraphQLThrowableOptions;
107
+ #biMapper: BiMapper<I>;
108
+
109
+ #getConfirmedBoxes: OP<ConfirmedBoxesResponse, QueryBoxesArgs>;
110
+ #getUnconfirmedBoxes: OP<UnconfirmedBoxesResponse, QueryBoxesArgs>;
111
+ #getAllBoxes: OP<CombinedBoxesResponse, QueryBoxesArgs>;
112
+ #getConfirmedTransactions: OP<ConfirmedTxResponse, QueryTransactionsArgs>;
113
+ #getUnconfirmedTransactions: OP<UnconfirmedTxResponse, MempoolTransactionsArgs>;
114
+ #checkTransaction: OP<CheckTransactionResponse, SignedTxArgsResp>;
115
+ #sendTransaction: OP<TransactionSubmissionResponse, SignedTxArgsResp>;
116
+ #getHeaders!: OP<BlockHeadersResponse, QueryBlockHeadersArgs>;
117
+
118
+ constructor(url: string);
119
+ constructor(options: ErgoGraphQLRequestOptions);
120
+ constructor(optOrUrl: ErgoGraphQLRequestOptions | string) {
121
+ this.#biMapper = (value) => BigInt(value) as I;
89
122
  this.#options = {
90
123
  ...(isRequestParam(optOrUrl) ? optOrUrl : { url: optOrUrl }),
91
124
  throwOnNonNetworkErrors: true
92
125
  };
93
126
 
94
- this.#getConfBoxes = this.createOperation<
95
- ConfirmedBoxesResponse,
96
- QueryBoxesArgs
97
- >(CONF_BOXES_QUERY);
98
-
99
- this.#getUnconfBoxes = this.createOperation<
100
- UnconfirmedBoxesResponse,
101
- QueryBoxesArgs
102
- >(UNCONF_BOXES_QUERY);
103
-
104
- this.#getAllBoxes = this.createOperation<
105
- CombinedBoxesResponse,
106
- QueryBoxesArgs
107
- >(ALL_BOXES_QUERY);
108
-
109
- this.#getHeaders = this.createOperation<
110
- BlockHeadersResponse,
111
- QueryBlockHeadersArgs
112
- >(HEADERS_QUERY);
113
-
114
- this.#checkTx = this.createOperation<
115
- CheckTransactionResponse,
116
- SignedTxArgsResp
117
- >(CHECK_TX_MUTATION);
118
-
119
- this.#sendTx = this.createOperation<
120
- TransactionSubmissionResponse,
121
- SignedTxArgsResp
122
- >(SEND_TX_MUTATION);
127
+ this.#getConfirmedBoxes = this.createOperation(CONF_BOXES_QUERY);
128
+ this.#getUnconfirmedBoxes = this.createOperation(UNCONF_BOXES_QUERY);
129
+ this.#getAllBoxes = this.createOperation(ALL_BOXES_QUERY);
130
+ this.#getConfirmedTransactions = this.createOperation(CONF_TX_QUERY);
131
+ this.#getUnconfirmedTransactions = this.createOperation(UNCONF_TX_QUERY);
132
+ this.#checkTransaction = this.createOperation(CHECK_TX_MUTATION);
133
+ this.#sendTransaction = this.createOperation(SEND_TX_MUTATION);
134
+ this.#getHeaders = this.createOperation(HEADERS_QUERY);
123
135
  }
124
136
 
125
137
  #fetchBoxes(args: QueryBoxesArgs, inclConf: boolean, inclUnconf: boolean) {
126
138
  return inclConf && inclUnconf
127
139
  ? this.#getAllBoxes(args)
128
140
  : inclUnconf
129
- ? this.#getUnconfBoxes(args)
130
- : this.#getConfBoxes(args);
141
+ ? this.#getUnconfirmedBoxes(args)
142
+ : this.#getConfirmedBoxes(args);
143
+ }
144
+
145
+ setUrl(url: string): ErgoGraphQLProvider<I> {
146
+ this.#options.url = url;
147
+ return this;
131
148
  }
132
149
 
133
- async *streamBoxes(
134
- query: GraphQLBoxQuery
135
- ): AsyncGenerator<ChainProviderBox[]> {
150
+ setBigIntMapper<M>(mapper: BiMapper<M>): ErgoGraphQLProvider<M> {
151
+ this.#biMapper = mapper as unknown as BiMapper<I>;
152
+ return this as unknown as ErgoGraphQLProvider<M>;
153
+ }
154
+
155
+ async *streamBoxes(query: GraphQLBoxQuery): AsyncGenerator<ChainProviderBox<I>[]> {
136
156
  if (isEmpty(query.where)) {
137
157
  throw new Error("Cannot fetch unspent boxes without a where clause.");
138
158
  }
139
159
 
140
- const notBeingSpent = (box: Box) => !box.beingSpent;
160
+ const notBeingSpent = (box: GQLBox) => !box.beingSpent;
141
161
  const returnedBoxIds = new Set<string>();
142
162
  const { where, from } = query;
143
- const args = buildGqlBoxQueryArgs(where);
163
+ const queries = buildGqlBoxQueries(where);
164
+ const isMempoolAware = from !== "blockchain";
144
165
 
145
- let inclChain = from !== "mempool";
146
- let inclPool = from !== "blockchain";
147
- const isMempoolAware = inclPool;
166
+ for (const query of queries) {
167
+ let inclChain = from !== "mempool";
168
+ let inclPool = from !== "blockchain";
148
169
 
149
- do {
150
- const response = await this.#fetchBoxes(args, inclChain, inclPool);
170
+ while (inclChain || inclPool) {
171
+ const { data } = await this.#fetchBoxes(query, inclChain, inclPool);
172
+ let boxes: ChainProviderBox<I>[] = [];
151
173
 
152
- const { data } = response;
153
- let boxes: ChainProviderBox[] = [];
174
+ if (inclChain && hasConfirmed(data)) {
175
+ if (some(data.boxes)) {
176
+ const confirmedBoxes = (
177
+ isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes
178
+ ).map((b) => mapConfirmedBox(b, this.#biMapper));
154
179
 
155
- if (inclChain && hasConfirmed(data)) {
156
- if (some(data.boxes)) {
157
- const confirmedBoxes = (
158
- isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes
159
- ).map(asConfirmed(true));
180
+ boxes = boxes.concat(confirmedBoxes);
181
+ }
160
182
 
161
- boxes = boxes.concat(confirmedBoxes);
183
+ inclChain = data.boxes.length === PAGE_SIZE;
162
184
  }
163
185
 
164
- inclChain = data.boxes.length === PAGE_SIZE;
165
- }
186
+ if (isMempoolAware && hasMempool(data)) {
187
+ if (some(data.mempool.boxes)) {
188
+ const mempoolBoxes = data.mempool.boxes
189
+ .filter(notBeingSpent)
190
+ .map((b) => mapUnconfirmedBox(b, this.#biMapper));
191
+ boxes = boxes.concat(mempoolBoxes);
192
+ }
193
+
194
+ inclPool = data.mempool.boxes.length === PAGE_SIZE;
195
+ }
166
196
 
167
- if (isMempoolAware && hasMempool(data)) {
168
- if (some(data.mempool.boxes)) {
169
- const mempoolBoxes = data.mempool.boxes
170
- .filter(notBeingSpent)
171
- .map(asConfirmed(false));
172
- boxes = boxes.concat(mempoolBoxes);
197
+ if (some(boxes)) {
198
+ // boxes can be moved from the mempool to the blockchain while streaming,
199
+ // so we need to filter out boxes that have already been returned.
200
+ if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {
201
+ boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId));
202
+ }
203
+
204
+ if (some(boxes)) {
205
+ boxes = uniqBy(boxes, (box) => box.boxId);
206
+ for (const box of boxes) returnedBoxIds.add(box.boxId);
207
+ yield boxes;
208
+ }
173
209
  }
174
210
 
175
- inclPool = data.mempool.boxes.length === PAGE_SIZE;
211
+ if (inclChain || inclPool) query.skip += PAGE_SIZE;
176
212
  }
213
+ }
214
+ }
177
215
 
178
- if (some(boxes)) {
179
- // boxes can be moved from the mempool to the blockchain while streaming,
180
- // so we need to filter out boxes that have already been returned.
181
- if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {
182
- boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId));
183
- }
216
+ async getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox<I>[]> {
217
+ const boxes: ChainProviderBox<I>[][] = [];
218
+ for await (const chunk of this.streamBoxes(query)) boxes.push(chunk);
219
+ return orderBy(boxes.flat(), (box) => box.creationHeight);
220
+ }
184
221
 
185
- if (some(boxes)) {
186
- boxes = uniqBy(boxes, (box) => box.boxId);
187
- for (const box of boxes) returnedBoxIds.add(box.boxId);
188
- yield boxes;
222
+ async *streamUnconfirmedTransactions(
223
+ query: TransactionQuery<GraphQLUnconfirmedTransactionWhere>
224
+ ): AsyncIterable<ChainProviderUnconfirmedTransaction<I>[]> {
225
+ const queries = buildGqlUnconfirmedTxQueries(query.where);
226
+
227
+ for (const query of queries) {
228
+ let keepFetching = true;
229
+ while (keepFetching) {
230
+ const response = await this.#getUnconfirmedTransactions(query);
231
+ if (some(response.data?.mempool?.transactions)) {
232
+ yield response.data.mempool.transactions.map((t) =>
233
+ mapUnconfirmedTransaction(t, this.#biMapper)
234
+ );
189
235
  }
236
+
237
+ keepFetching = response.data?.mempool?.transactions?.length === PAGE_SIZE;
238
+ if (keepFetching) query.skip += PAGE_SIZE;
190
239
  }
240
+ }
241
+ }
242
+
243
+ async getUnconfirmedTransactions(
244
+ query: TransactionQuery<GraphQLUnconfirmedTransactionWhere>
245
+ ): Promise<ChainProviderUnconfirmedTransaction<I>[]> {
246
+ const transactions: ChainProviderUnconfirmedTransaction<I>[][] = [];
247
+ for await (const chunk of this.streamUnconfirmedTransactions(query)) {
248
+ transactions.push(chunk);
249
+ }
250
+
251
+ return transactions.flat();
252
+ }
253
+
254
+ async *streamConfirmedTransactions(
255
+ query: TransactionQuery<GraphQLConfirmedTransactionWhere>
256
+ ): AsyncIterable<ChainProviderConfirmedTransaction<I>[]> {
257
+ const queries = buildGqlConfirmedTxQueries(query.where);
258
+
259
+ for (const query of queries) {
260
+ let keepFetching = true;
261
+ while (keepFetching) {
262
+ const response = await this.#getConfirmedTransactions(query);
263
+ if (some(response.data?.transactions)) {
264
+ yield response.data.transactions.map((t) =>
265
+ mapConfirmedTransaction(t, this.#biMapper)
266
+ );
267
+ }
191
268
 
192
- if (inclChain || inclPool) args.skip += PAGE_SIZE;
193
- } while (inclChain || inclPool);
269
+ keepFetching = response.data?.transactions?.length === PAGE_SIZE;
270
+ if (keepFetching) query.skip += PAGE_SIZE;
271
+ }
272
+ }
194
273
  }
195
274
 
196
- async getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox[]> {
197
- let boxes: ChainProviderBox[] = [];
198
- for await (const chunk of this.streamBoxes(query)) {
199
- boxes = boxes.concat(chunk);
275
+ async getConfirmedTransactions(
276
+ query: TransactionQuery<GraphQLConfirmedTransactionWhere>
277
+ ): Promise<ChainProviderConfirmedTransaction<I>[]> {
278
+ const transactions: ChainProviderConfirmedTransaction<I>[][] = [];
279
+ for await (const chunk of this.streamConfirmedTransactions(query)) {
280
+ transactions.push(chunk);
200
281
  }
201
282
 
202
- return orderBy(boxes, (box) => box.creationHeight);
283
+ return transactions.flat();
203
284
  }
204
285
 
205
286
  async getHeaders(query: HeaderQuery): Promise<BlockHeader[]> {
206
287
  const response = await this.#getHeaders(query);
207
288
 
208
289
  return (
209
- response.data?.blockHeaders.map((header) => ({
210
- ...header,
211
- id: header.headerId,
212
- timestamp: Number(header.timestamp),
213
- nBits: Number(header.nBits),
214
- votes: header.votes.join("")
290
+ response.data?.blockHeaders.map((h) => ({
291
+ ...h,
292
+ id: h.headerId,
293
+ timestamp: Number(h.timestamp),
294
+ nBits: Number(h.nBits),
295
+ votes: hex.encode(Uint8Array.from(h.votes))
215
296
  })) ?? []
216
297
  );
217
298
  }
@@ -230,8 +311,7 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
230
311
  signedTransaction: SignedTransaction
231
312
  ): Promise<TransactionEvaluationResult> {
232
313
  try {
233
- const response = await this.#checkTx({ signedTransaction });
234
-
314
+ const response = await this.#checkTransaction({ signedTransaction });
235
315
  return { success: true, transactionId: response.data.checkTransaction };
236
316
  } catch (e) {
237
317
  return { success: false, message: (e as Error).message };
@@ -242,8 +322,7 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
242
322
  signedTransaction: SignedTransaction
243
323
  ): Promise<TransactionEvaluationResult> {
244
324
  try {
245
- const response = await this.#sendTx({ signedTransaction });
246
-
325
+ const response = await this.#sendTransaction({ signedTransaction });
247
326
  return { success: true, transactionId: response.data.submitTransaction };
248
327
  } catch (e) {
249
328
  return { success: false, message: (e as Error).message };
@@ -251,37 +330,58 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
251
330
  }
252
331
 
253
332
  reduceTransaction(): Promise<TransactionReductionResult> {
254
- throw new NotSupportedError(
255
- "Transaction reducing is not supported by ergo-graphql."
256
- );
333
+ throw new NotSupportedError("Transaction reducing is not supported by ergo-graphql.");
257
334
  }
258
335
  }
259
336
 
260
- function buildGqlBoxQueryArgs(where: GraphQLBoxWhere) {
261
- const args = {
337
+ function buildGqlBoxQueries(where: GraphQLBoxWhere) {
338
+ const ergoTrees = uniq(
339
+ [
340
+ merge(where.ergoTrees, where.ergoTree) ?? [],
341
+ merge(where.addresses, where.address)?.map((a) =>
342
+ typeof a === "string" ? ErgoAddress.decode(a).ergoTree : a.ergoTree
343
+ ) ?? []
344
+ ].flat()
345
+ );
346
+
347
+ return chunk(ergoTrees, MAX_ARGS).map((chunk) => ({
262
348
  spent: false,
263
- boxIds: merge(where.boxIds, where.boxId),
264
- ergoTrees: merge(where.ergoTrees, where.ergoTree),
349
+ boxIds: where.boxId ? [where.boxId] : undefined,
350
+ ergoTrees: chunk,
265
351
  ergoTreeTemplateHash: where.templateHash,
266
352
  tokenId: where.tokenId,
267
353
  skip: 0,
268
354
  take: PAGE_SIZE
269
- } satisfies QueryBoxesArgs;
270
-
271
- const addresses = merge(where.addresses, where.address);
272
- if (some(addresses)) {
273
- const trees = addresses.map((address) =>
274
- typeof address === "string"
275
- ? ErgoAddress.fromBase58(address).ergoTree
276
- : address.ergoTree
277
- );
355
+ }));
356
+ }
278
357
 
279
- args.ergoTrees = uniq(
280
- some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees
281
- );
282
- }
358
+ function buildGqlUnconfirmedTxQueries(where: GraphQLConfirmedTransactionWhere) {
359
+ const addresses = uniq(
360
+ [
361
+ merge(where.addresses, where.address)?.map((address): string =>
362
+ typeof address === "string" ? address : address.encode()
363
+ ) ?? [],
364
+ merge(where.ergoTrees, where.ergoTree)?.map((tree) =>
365
+ ErgoAddress.fromErgoTree(tree).encode()
366
+ ) ?? []
367
+ ].flat()
368
+ );
369
+
370
+ return chunk(addresses, MAX_ARGS).map((chunk) => ({
371
+ addresses: chunk.length ? chunk : undefined,
372
+ transactionIds: where.transactionId ? [where.transactionId] : undefined,
373
+ skip: 0,
374
+ take: PAGE_SIZE
375
+ }));
376
+ }
283
377
 
284
- return args;
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
384
+ }));
285
385
  }
286
386
 
287
387
  function merge<T>(array?: T[], el?: T) {
@@ -300,14 +400,83 @@ function hasConfirmed(data: unknown): data is ConfirmedBoxesResponse {
300
400
  return !!(data as ConfirmedBoxesResponse)?.boxes;
301
401
  }
302
402
 
303
- function asConfirmed(confirmed: boolean) {
304
- return (box: Box): ChainProviderBox => ({
305
- ...box,
306
- value: BigInt(box.value),
307
- assets: box.assets.map((asset) => ({
308
- tokenId: asset.tokenId,
309
- amount: BigInt(asset.amount)
403
+ function mapConfirmedBox<T>(box: GQLBox, mapper: BiMapper<T>): ChainProviderBox<T> {
404
+ const mapped = mapBox(box, mapper) as ChainProviderBox<T>;
405
+ mapped.confirmed = true;
406
+ return mapped;
407
+ }
408
+
409
+ function mapUnconfirmedBox<T>(box: GQLBox, mapper: BiMapper<T>): ChainProviderBox<T> {
410
+ const mapped = mapBox(box, mapper) as ChainProviderBox<T>;
411
+ mapped.confirmed = false;
412
+ return mapped;
413
+ }
414
+
415
+ function mapBox<T>(
416
+ box: GQLBox | GQLUnconfirmedBox,
417
+ mapper: BiMapper<T>
418
+ ): Omit<ChainProviderBox<T>, "confirmed"> {
419
+ return {
420
+ boxId: box.boxId,
421
+ transactionId: box.transactionId,
422
+ value: mapper(box.value),
423
+ ergoTree: box.ergoTree,
424
+ assets: box.assets.map((t) => ({ tokenId: t.tokenId, amount: mapper(t.amount) })),
425
+ creationHeight: box.creationHeight,
426
+ additionalRegisters: box.additionalRegisters,
427
+ index: box.index
428
+ };
429
+ }
430
+
431
+ function mapUnconfirmedTransaction<T>(
432
+ tx: UnconfirmedTransaction,
433
+ mapper: BiMapper<T>
434
+ ): ChainProviderUnconfirmedTransaction<T> {
435
+ return {
436
+ transactionId: tx.transactionId,
437
+ timestamp: Number(tx.timestamp),
438
+ inputs: tx.inputs.map((i) => ({
439
+ spendingProof: {
440
+ // biome-ignore lint/style/noNonNullAssertion: bad type declarations at '@ergo-graphql/type'
441
+ extension: i.extension!,
442
+ // biome-ignore lint/style/noNonNullAssertion: bad type declarations at '@ergo-graphql/type'
443
+ proofBytes: i.proofBytes!
444
+ },
445
+ // biome-ignore lint/style/noNonNullAssertion: bad type declarations at '@ergo-graphql/type'
446
+ ...mapBox(i.box!, mapper)
310
447
  })),
311
- confirmed
312
- });
448
+ dataInputs: tx.dataInputs.map((di) => ({ boxId: di.boxId })),
449
+ outputs: tx.outputs.map((b) => mapBox(b, mapper)),
450
+ confirmed: false
451
+ };
452
+ }
453
+
454
+ function mapConfirmedTransaction<T>(
455
+ tx: Transaction,
456
+ mapper: BiMapper<T>
457
+ ): ChainProviderConfirmedTransaction<T> {
458
+ return {
459
+ transactionId: tx.transactionId,
460
+ timestamp: Number(tx.timestamp),
461
+ inputs: tx.inputs.map((i) => ({
462
+ spendingProof: {
463
+ // biome-ignore lint/style/noNonNullAssertion: bad type declarations at '@ergo-graphql/type'
464
+ extension: i.extension!,
465
+ // biome-ignore lint/style/noNonNullAssertion: bad type declarations at '@ergo-graphql/type'
466
+ proofBytes: i.proofBytes!
467
+ },
468
+ // biome-ignore lint/style/noNonNullAssertion: bad type declarations at '@ergo-graphql/type'
469
+ ...mapBox(i.box!, mapper)
470
+ })),
471
+ dataInputs: tx.dataInputs.map((di) => ({ boxId: di.boxId })),
472
+ outputs: tx.outputs.map((b) => mapBox(b, mapper)),
473
+ height: tx.inclusionHeight,
474
+ headerId: tx.headerId,
475
+ index: tx.index,
476
+ confirmed: true
477
+ };
478
+ }
479
+
480
+ export function isRequestParam(obj: unknown): obj is ErgoGraphQLRequestOptions {
481
+ return typeof obj === "object" && (obj as ErgoGraphQLRequestOptions).url !== undefined;
313
482
  }
@@ -1,15 +1,24 @@
1
1
  const B = [
2
- "query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)",
2
+ "$boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int",
3
3
  "boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take",
4
- "boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent"
4
+ "boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters"
5
5
  ];
6
6
 
7
- export const CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;
8
- export const UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;
9
- export const ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;
7
+ export const CONF_BOXES_QUERY = `query boxes($spent: Boolean! ${B[0]}) { boxes(spent: $spent ${B[1]}) { ${B[2]} beingSpent } }`;
8
+ export const UNCONF_BOXES_QUERY = `query boxes(${B[0]}) { mempool { boxes(${B[1]}) { ${B[2]} beingSpent } } }`;
9
+ export const ALL_BOXES_QUERY = `query boxes($spent: Boolean! ${B[0]}) { boxes(spent: $spent ${B[1]}) { ${B[2]} beingSpent } mempool { boxes(${B[1]}) { ${B[2]} beingSpent } } }`;
10
+
10
11
  export const HEADERS_QUERY =
11
12
  "query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }";
12
13
  export const CHECK_TX_MUTATION =
13
14
  "mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }";
14
15
  export const SEND_TX_MUTATION =
15
16
  "mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }";
17
+
18
+ const T = [
19
+ "$addresses: [String!], $transactionIds: [String!], $skip: Int, $take: Int",
20
+ "addresses: $addresses, transactionIds: $transactionIds, skip: $skip, take: $take",
21
+ `transactionId timestamp inputs { proofBytes extension index box { ${B[2]} } } dataInputs { boxId }`
22
+ ];
23
+ export const CONF_TX_QUERY = `query confirmedTransactions(${T[0]} $relevantOnly: Boolean) { transactions(${T[1]}) { ${T[2]} outputs(relevantOnly: $relevantOnly) { ${B[2]} } inclusionHeight headerId index } }`;
24
+ export const UNCONF_TX_QUERY = `query unconfirmedTransactions(${T[0]}) { mempool { transactions(${T[1]}) { ${T[2]} outputs { ${B[2]} } } } }`;
package/src/index.ts CHANGED
@@ -1 +1,4 @@
1
1
  export * from "./ergo-graphql/ergoGraphQLProvider";
2
+ export * from "./types/blockchainProvider";
3
+ export * from "./utils/networking";
4
+ export * from "./utils/graphql";