@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.
- package/CHANGELOG.md +23 -0
- package/dist/index.d.mts +156 -37
- package/dist/index.d.ts +156 -37
- package/dist/index.js +349 -105
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +345 -107
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/ergo-graphql/ergoGraphQLProvider.ts +312 -143
- package/src/ergo-graphql/queries.ts +14 -5
- package/src/index.ts +3 -0
- package/src/types/blockchainProvider.ts +101 -18
- package/src/utils/_tests.ts +6 -10
- package/src/utils/graphql.ts +53 -65
- package/src/utils/networking.ts +83 -0
@@ -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
|
-
|
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
|
-
|
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
|
-
"
|
82
|
+
"throwOnNonNetworkErrors"
|
64
83
|
>;
|
65
84
|
|
66
|
-
type ConfirmedBoxesResponse = { boxes:
|
67
|
-
type UnconfirmedBoxesResponse = { mempool: { boxes:
|
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
|
-
|
95
|
+
type GraphQLThrowableOptions = ErgoGraphQLRequestOptions & {
|
96
|
+
throwOnNonNetworkErrors: true;
|
97
|
+
};
|
75
98
|
|
76
|
-
|
77
|
-
|
99
|
+
type OP<R, V extends GraphQLVariables> = GraphQLOperation<GraphQLSuccessResponse<R>, V>;
|
100
|
+
type BiMapper<T> = (value: string) => T;
|
78
101
|
|
79
|
-
|
80
|
-
|
81
|
-
#getAllBoxes;
|
82
|
-
#getHeaders;
|
83
|
-
#checkTx;
|
84
|
-
#sendTx;
|
102
|
+
const PAGE_SIZE = 50;
|
103
|
+
const MAX_ARGS = 20;
|
85
104
|
|
86
|
-
|
87
|
-
|
88
|
-
|
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.#
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
this.#
|
100
|
-
|
101
|
-
|
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.#
|
130
|
-
: this.#
|
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
|
-
|
134
|
-
|
135
|
-
|
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:
|
160
|
+
const notBeingSpent = (box: GQLBox) => !box.beingSpent;
|
141
161
|
const returnedBoxIds = new Set<string>();
|
142
162
|
const { where, from } = query;
|
143
|
-
const
|
163
|
+
const queries = buildGqlBoxQueries(where);
|
164
|
+
const isMempoolAware = from !== "blockchain";
|
144
165
|
|
145
|
-
|
146
|
-
|
147
|
-
|
166
|
+
for (const query of queries) {
|
167
|
+
let inclChain = from !== "mempool";
|
168
|
+
let inclPool = from !== "blockchain";
|
148
169
|
|
149
|
-
|
150
|
-
|
170
|
+
while (inclChain || inclPool) {
|
171
|
+
const { data } = await this.#fetchBoxes(query, inclChain, inclPool);
|
172
|
+
let boxes: ChainProviderBox<I>[] = [];
|
151
173
|
|
152
|
-
|
153
|
-
|
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
|
-
|
156
|
-
|
157
|
-
const confirmedBoxes = (
|
158
|
-
isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes
|
159
|
-
).map(asConfirmed(true));
|
180
|
+
boxes = boxes.concat(confirmedBoxes);
|
181
|
+
}
|
160
182
|
|
161
|
-
|
183
|
+
inclChain = data.boxes.length === PAGE_SIZE;
|
162
184
|
}
|
163
185
|
|
164
|
-
|
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
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
.
|
172
|
-
|
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
|
211
|
+
if (inclChain || inclPool) query.skip += PAGE_SIZE;
|
176
212
|
}
|
213
|
+
}
|
214
|
+
}
|
177
215
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
193
|
-
|
269
|
+
keepFetching = response.data?.transactions?.length === PAGE_SIZE;
|
270
|
+
if (keepFetching) query.skip += PAGE_SIZE;
|
271
|
+
}
|
272
|
+
}
|
194
273
|
}
|
195
274
|
|
196
|
-
async
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
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((
|
210
|
-
...
|
211
|
-
id:
|
212
|
-
timestamp: Number(
|
213
|
-
nBits: Number(
|
214
|
-
votes:
|
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.#
|
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.#
|
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
|
261
|
-
const
|
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:
|
264
|
-
ergoTrees:
|
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
|
-
}
|
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
|
-
|
280
|
-
|
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
|
-
|
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
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
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
|
-
|
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
|
-
"
|
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
|
4
|
+
"boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters"
|
5
5
|
];
|
6
6
|
|
7
|
-
export const CONF_BOXES_QUERY =
|
8
|
-
export const UNCONF_BOXES_QUERY =
|
9
|
-
export const ALL_BOXES_QUERY =
|
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