@fleet-sdk/blockchain-providers 0.4.0 → 0.5.0
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 +15 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +31 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/ergo-graphql/ergoGraphQLProvider.ts +90 -57
- package/src/ergo-graphql/queries.ts +9 -6
- package/src/index.ts +1 -1
- package/src/types/blockchainProvider.ts +18 -8
- package/src/utils/_tests.ts +6 -2
- package/src/utils/graphql.test-d.ts +15 -7
- package/src/utils/graphql.ts +34 -12
- package/src/ergo-graphql/index.ts +0 -1
- package/src/types/index.ts +0 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# @fleet-sdk/blockchain-providers
|
2
2
|
|
3
|
+
## 0.5.0
|
4
|
+
|
5
|
+
### Patch Changes
|
6
|
+
|
7
|
+
- Updated dependencies [8f14d37]
|
8
|
+
- @fleet-sdk/core@0.5.0
|
9
|
+
|
10
|
+
## 0.4.1
|
11
|
+
|
12
|
+
### Patch Changes
|
13
|
+
|
14
|
+
- Updated dependencies [28e3467]
|
15
|
+
- @fleet-sdk/common@0.4.1
|
16
|
+
- @fleet-sdk/core@0.4.1
|
17
|
+
|
3
18
|
## 0.4.0
|
4
19
|
|
5
20
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
@@ -278,4 +278,4 @@ declare class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
|
278
278
|
reduceTransaction(): Promise<TransactionReductionResult>;
|
279
279
|
}
|
280
280
|
|
281
|
-
export { ErgoGraphQLProvider, ErgoGraphQLRequestOptions, GraphQLBoxQuery, GraphQLBoxWhere };
|
281
|
+
export { ErgoGraphQLProvider, type ErgoGraphQLRequestOptions, type GraphQLBoxQuery, type GraphQLBoxWhere };
|
package/dist/index.d.ts
CHANGED
@@ -278,4 +278,4 @@ declare class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
|
278
278
|
reduceTransaction(): Promise<TransactionReductionResult>;
|
279
279
|
}
|
280
280
|
|
281
|
-
export { ErgoGraphQLProvider, ErgoGraphQLRequestOptions, GraphQLBoxQuery, GraphQLBoxWhere };
|
281
|
+
export { ErgoGraphQLProvider, type ErgoGraphQLRequestOptions, type GraphQLBoxQuery, type GraphQLBoxWhere };
|
package/dist/index.js
CHANGED
@@ -22,9 +22,13 @@ function createGqlOperation(query, options) {
|
|
22
22
|
})
|
23
23
|
});
|
24
24
|
const rawData = await response.text();
|
25
|
-
const parsedData = (options.parser ?? JSON).parse(
|
25
|
+
const parsedData = (options.parser ?? JSON).parse(
|
26
|
+
rawData
|
27
|
+
);
|
26
28
|
if (options.throwOnNonNetworkErrors && common.some(parsedData.errors) && common.isEmpty(parsedData.data)) {
|
27
|
-
throw new common.BlockchainProviderError(parsedData.errors[0].message, {
|
29
|
+
throw new common.BlockchainProviderError(parsedData.errors[0].message, {
|
30
|
+
cause: parsedData.errors
|
31
|
+
});
|
28
32
|
}
|
29
33
|
return parsedData;
|
30
34
|
};
|
@@ -38,16 +42,16 @@ function isRequestParam(obj) {
|
|
38
42
|
|
39
43
|
// src/ergo-graphql/queries.ts
|
40
44
|
var B = [
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
"query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)",
|
46
|
+
"boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take",
|
47
|
+
"boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent"
|
44
48
|
];
|
45
49
|
var CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;
|
46
50
|
var UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
47
51
|
var ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
48
|
-
var HEADERS_QUERY =
|
49
|
-
var CHECK_TX_MUTATION =
|
50
|
-
var SEND_TX_MUTATION =
|
52
|
+
var HEADERS_QUERY = "query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }";
|
53
|
+
var CHECK_TX_MUTATION = "mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }";
|
54
|
+
var SEND_TX_MUTATION = "mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }";
|
51
55
|
|
52
56
|
// src/ergo-graphql/ergoGraphQLProvider.ts
|
53
57
|
var PAGE_SIZE = 50;
|
@@ -72,13 +76,7 @@ var ErgoGraphQLProvider = class {
|
|
72
76
|
this.#sendTx = this.createOperation(SEND_TX_MUTATION);
|
73
77
|
}
|
74
78
|
#fetchBoxes(args, inclConf, inclUnconf) {
|
75
|
-
|
76
|
-
return this.#getAllBoxes(args);
|
77
|
-
} else if (inclUnconf) {
|
78
|
-
return this.#getUnconfBoxes(args);
|
79
|
-
} else {
|
80
|
-
return this.#getConfBoxes(args);
|
81
|
-
}
|
79
|
+
return inclConf && inclUnconf ? this.#getAllBoxes(args) : inclUnconf ? this.#getUnconfBoxes(args) : this.#getConfBoxes(args);
|
82
80
|
}
|
83
81
|
async *streamBoxes(query) {
|
84
82
|
if (common.isEmpty(query.where)) {
|
@@ -88,26 +86,26 @@ var ErgoGraphQLProvider = class {
|
|
88
86
|
const returnedBoxIds = /* @__PURE__ */ new Set();
|
89
87
|
const { where, from } = query;
|
90
88
|
const args = buildGqlBoxQueryArgs(where);
|
91
|
-
let
|
92
|
-
let
|
93
|
-
const isMempoolAware =
|
89
|
+
let inclChain = from !== "mempool";
|
90
|
+
let inclPool = from !== "blockchain";
|
91
|
+
const isMempoolAware = inclPool;
|
94
92
|
do {
|
95
|
-
const response = await this.#fetchBoxes(args,
|
93
|
+
const response = await this.#fetchBoxes(args, inclChain, inclPool);
|
96
94
|
const { data } = response;
|
97
95
|
let boxes = [];
|
98
|
-
if (
|
96
|
+
if (inclChain && hasConfirmed(data)) {
|
99
97
|
if (common.some(data.boxes)) {
|
100
98
|
const confirmedBoxes = (isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes).map(asConfirmed(true));
|
101
99
|
boxes = boxes.concat(confirmedBoxes);
|
102
100
|
}
|
103
|
-
|
101
|
+
inclChain = data.boxes.length === PAGE_SIZE;
|
104
102
|
}
|
105
103
|
if (isMempoolAware && hasMempool(data)) {
|
106
104
|
if (common.some(data.mempool.boxes)) {
|
107
105
|
const mempoolBoxes = data.mempool.boxes.filter(notBeingSpent).map(asConfirmed(false));
|
108
106
|
boxes = boxes.concat(mempoolBoxes);
|
109
107
|
}
|
110
|
-
|
108
|
+
inclPool = data.mempool.boxes.length === PAGE_SIZE;
|
111
109
|
}
|
112
110
|
if (common.some(boxes)) {
|
113
111
|
if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {
|
@@ -115,13 +113,12 @@ var ErgoGraphQLProvider = class {
|
|
115
113
|
}
|
116
114
|
if (common.some(boxes)) {
|
117
115
|
boxes = common.uniqBy(boxes, (box) => box.boxId);
|
118
|
-
|
116
|
+
for (const box of boxes) returnedBoxIds.add(box.boxId);
|
119
117
|
yield boxes;
|
120
118
|
}
|
121
119
|
}
|
122
|
-
if (
|
123
|
-
|
124
|
-
} while (fetchFromChain || fetchFromMempool);
|
120
|
+
if (inclChain || inclPool) args.skip += PAGE_SIZE;
|
121
|
+
} while (inclChain || inclPool);
|
125
122
|
}
|
126
123
|
async getBoxes(query) {
|
127
124
|
let boxes = [];
|
@@ -162,7 +159,9 @@ var ErgoGraphQLProvider = class {
|
|
162
159
|
}
|
163
160
|
}
|
164
161
|
reduceTransaction() {
|
165
|
-
throw new common.NotSupportedError(
|
162
|
+
throw new common.NotSupportedError(
|
163
|
+
"Transaction reducing is not supported by ergo-graphql."
|
164
|
+
);
|
166
165
|
}
|
167
166
|
};
|
168
167
|
function buildGqlBoxQueryArgs(where) {
|
@@ -180,16 +179,16 @@ function buildGqlBoxQueryArgs(where) {
|
|
180
179
|
const trees = addresses.map(
|
181
180
|
(address) => typeof address === "string" ? core.ErgoAddress.fromBase58(address).ergoTree : address.ergoTree
|
182
181
|
);
|
183
|
-
args.ergoTrees = common.uniq(
|
182
|
+
args.ergoTrees = common.uniq(
|
183
|
+
common.some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees
|
184
|
+
);
|
184
185
|
}
|
185
186
|
return args;
|
186
187
|
}
|
187
188
|
function merge(array, el) {
|
188
|
-
if (common.isEmpty(array) && common.isUndefined(el))
|
189
|
-
return;
|
189
|
+
if (common.isEmpty(array) && common.isUndefined(el)) return;
|
190
190
|
const set = new Set(array ?? []);
|
191
|
-
if (!common.isUndefined(el))
|
192
|
-
set.add(el);
|
191
|
+
if (!common.isUndefined(el)) set.add(el);
|
193
192
|
return Array.from(set.values());
|
194
193
|
}
|
195
194
|
function hasMempool(data) {
|
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/ergo-graphql/ergoGraphQLProvider.ts","../src/utils/graphql.ts","../src/ergo-graphql/queries.ts"],"names":["ensureDefaults","isEmpty","some"],"mappings":"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","sourcesContent":["import {\n Box,\n QueryBoxesArgs as BoxesArgs,\n Header,\n QueryBlockHeadersArgs as HeadersArgs\n} from \"@ergo-graphql/types\";\nimport {\n Base58String,\n BlockHeader,\n ensureDefaults,\n HexString,\n isEmpty,\n isUndefined,\n NotSupportedError,\n orderBy,\n SignedTransaction,\n some,\n uniq,\n uniqBy\n} from \"@fleet-sdk/common\";\nimport { ErgoAddress } from \"@fleet-sdk/core\";\nimport {\n BoxQuery,\n BoxWhere,\n ChainProviderBox,\n HeaderQuery,\n IBlockchainProvider,\n TransactionEvaluationResult,\n TransactionReductionResult\n} from \"../types\";\nimport {\n createGqlOperation,\n GraphQLOperation,\n GraphQLRequestOptions,\n GraphQLSuccessResponse,\n GraphQLThrowableOptions,\n GraphQLVariables,\n isRequestParam\n} from \"../utils\";\nimport {\n ALL_BOXES_QUERY,\n CHECK_TX_MUTATION,\n CONF_BOXES_QUERY,\n HEADERS_QUERY,\n SEND_TX_MUTATION,\n UNCONF_BOXES_QUERY\n} from \"./queries\";\n\nexport type GraphQLBoxWhere = BoxWhere & {\n /** Base16-encoded BoxIds */\n boxIds?: HexString[];\n\n /** Base16-encoded ErgoTrees */\n ergoTrees?: HexString[];\n\n /** Base58-encoded addresses or `ErgoAddress` objects */\n addresses?: (Base58String | ErgoAddress)[];\n};\n\nexport type GraphQLBoxQuery = BoxQuery<GraphQLBoxWhere>;\nexport type ErgoGraphQLRequestOptions = Omit<GraphQLRequestOptions, \"throwOnNonNetworkError\">;\n\ntype ConfBoxesResp = { boxes: Box[] };\ntype UnconfBoxesResp = { mempool: { boxes: Box[] } };\ntype AllBoxesResp = ConfBoxesResp & UnconfBoxesResp;\ntype HeadersResp = { blockHeaders: Header[] };\ntype CheckTxResp = { checkTransaction: string };\ntype SendTxResp = { submitTransaction: string };\ntype SignedTxArgsResp = { signedTransaction: SignedTransaction };\n\nconst PAGE_SIZE = 50;\n\nexport class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {\n #options: GraphQLThrowableOptions;\n\n #getConfBoxes;\n #getUnconfBoxes;\n #getAllBoxes;\n #getHeaders;\n #checkTx;\n #sendTx;\n\n constructor(url: string | URL);\n constructor(url: ErgoGraphQLRequestOptions);\n constructor(optOrUrl: ErgoGraphQLRequestOptions | string | URL) {\n this.#options = {\n ...(isRequestParam(optOrUrl) ? optOrUrl : { url: optOrUrl }),\n throwOnNonNetworkErrors: true\n };\n\n this.#getConfBoxes = this.createOperation<ConfBoxesResp, BoxesArgs>(CONF_BOXES_QUERY);\n this.#getUnconfBoxes = this.createOperation<UnconfBoxesResp, BoxesArgs>(UNCONF_BOXES_QUERY);\n this.#getAllBoxes = this.createOperation<AllBoxesResp, BoxesArgs>(ALL_BOXES_QUERY);\n this.#getHeaders = this.createOperation<HeadersResp, HeadersArgs>(HEADERS_QUERY);\n this.#checkTx = this.createOperation<CheckTxResp, SignedTxArgsResp>(CHECK_TX_MUTATION);\n this.#sendTx = this.createOperation<SendTxResp, SignedTxArgsResp>(SEND_TX_MUTATION);\n }\n\n #fetchBoxes(args: BoxesArgs, inclConf: boolean, inclUnconf: boolean) {\n if (inclConf && inclUnconf) {\n return this.#getAllBoxes(args);\n } else if (inclUnconf) {\n return this.#getUnconfBoxes(args);\n } else {\n return this.#getConfBoxes(args);\n }\n }\n\n async *streamBoxes(query: GraphQLBoxQuery): AsyncGenerator<ChainProviderBox[]> {\n if (isEmpty(query.where)) {\n throw new Error(\"Cannot fetch unspent boxes without a where clause.\");\n }\n\n const notBeingSpent = (box: Box) => !box.beingSpent;\n const returnedBoxIds = new Set<string>();\n const { where, from } = query;\n const args = buildGqlBoxQueryArgs(where);\n\n let fetchFromChain = from !== \"mempool\";\n let fetchFromMempool = from !== \"blockchain\";\n const isMempoolAware = fetchFromMempool;\n\n do {\n const response = await this.#fetchBoxes(args, fetchFromChain, fetchFromMempool);\n\n const { data } = response;\n let boxes: ChainProviderBox[] = [];\n\n if (fetchFromChain && hasConfirmed(data)) {\n if (some(data.boxes)) {\n const confirmedBoxes = (\n isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes\n ).map(asConfirmed(true));\n\n boxes = boxes.concat(confirmedBoxes);\n }\n\n fetchFromChain = data.boxes.length === PAGE_SIZE;\n }\n\n if (isMempoolAware && hasMempool(data)) {\n if (some(data.mempool.boxes)) {\n const mempoolBoxes = data.mempool.boxes.filter(notBeingSpent).map(asConfirmed(false));\n boxes = boxes.concat(mempoolBoxes);\n }\n\n fetchFromMempool = data.mempool.boxes.length === PAGE_SIZE;\n }\n\n if (some(boxes)) {\n // boxes can be moved from the mempool to the blockchain while streaming,\n // so we need to filter out boxes that have already been returned.\n if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {\n boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId));\n }\n\n if (some(boxes)) {\n boxes = uniqBy(boxes, (box) => box.boxId);\n boxes.forEach((box) => returnedBoxIds.add(box.boxId));\n\n yield boxes;\n }\n }\n\n if (fetchFromChain || fetchFromMempool) args.skip += PAGE_SIZE;\n } while (fetchFromChain || fetchFromMempool);\n }\n\n async getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox[]> {\n let boxes: ChainProviderBox[] = [];\n for await (const chunk of this.streamBoxes(query)) {\n boxes = boxes.concat(chunk);\n }\n\n return orderBy(boxes, (box) => box.creationHeight);\n }\n\n async getHeaders(query: HeaderQuery): Promise<BlockHeader[]> {\n const response = await this.#getHeaders(query);\n\n return (\n response.data?.blockHeaders.map((header) => ({\n ...header,\n id: header.headerId,\n timestamp: Number(header.timestamp),\n nBits: Number(header.nBits),\n votes: header.votes.join(\"\")\n })) ?? []\n );\n }\n\n createOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options?: Partial<ErgoGraphQLRequestOptions>\n ): GraphQLOperation<GraphQLSuccessResponse<R>, V> {\n const opt = ensureDefaults(options, this.#options);\n opt.throwOnNonNetworkErrors = true;\n\n return createGqlOperation(query, opt);\n }\n\n async checkTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#checkTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.checkTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n async submitTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#sendTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.submitTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n reduceTransaction(): Promise<TransactionReductionResult> {\n throw new NotSupportedError(\"Transaction reducing is not supported by ergo-graphql.\");\n }\n}\n\nfunction buildGqlBoxQueryArgs(where: GraphQLBoxWhere) {\n const args = {\n spent: false,\n boxIds: merge(where.boxIds, where.boxId),\n ergoTrees: merge(where.ergoTrees, where.ergoTree),\n ergoTreeTemplateHash: where.templateHash,\n tokenId: where.tokenId,\n skip: 0,\n take: PAGE_SIZE\n } satisfies BoxesArgs;\n\n const addresses = merge(where.addresses, where.address);\n if (some(addresses)) {\n const trees = addresses.map((address) =>\n typeof address === \"string\" ? ErgoAddress.fromBase58(address).ergoTree : address.ergoTree\n );\n\n args.ergoTrees = uniq(some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees);\n }\n\n return args;\n}\n\nfunction merge<T>(array?: T[], el?: T) {\n if (isEmpty(array) && isUndefined(el)) return;\n\n const set = new Set<T>(array ?? []);\n if (!isUndefined(el)) set.add(el);\n return Array.from(set.values());\n}\n\nfunction hasMempool(data: AllBoxesResp | ConfBoxesResp | UnconfBoxesResp): data is UnconfBoxesResp {\n return !!(data as UnconfBoxesResp)?.mempool?.boxes;\n}\n\nfunction hasConfirmed(data: AllBoxesResp | ConfBoxesResp | UnconfBoxesResp): data is ConfBoxesResp {\n return !!(data as ConfBoxesResp)?.boxes;\n}\n\nfunction asConfirmed(confirmed: boolean) {\n return (box: Box): ChainProviderBox => ({\n ...box,\n value: BigInt(box.value),\n assets: box.assets.map((asset) => ({\n tokenId: asset.tokenId,\n amount: BigInt(asset.amount)\n })),\n confirmed\n });\n}\n","import {\n BlockchainProviderError,\n clearUndefined,\n ensureDefaults,\n isEmpty,\n some\n} from \"@fleet-sdk/common\";\n\nconst OP_NAME_REGEX = /(query|mutation)\\s?([\\w\\-_]+)?/;\nexport const DEFAULT_HEADERS: Headers = {\n \"content-type\": \"application/json; charset=utf-8\",\n accept: \"application/graphql-response+json, application/json\"\n};\n\ntype Credentials = RequestCredentials;\ntype Headers = HeadersInit;\ntype Fetcher = typeof fetch;\n\nexport type GraphQLVariables = Record<string, unknown> | null;\n\nexport interface GraphQLError {\n message: string;\n}\n\nexport interface GraphQLSuccessResponse<T = unknown> {\n data: T;\n errors: null;\n}\n\nexport interface GraphQLErrorResponse {\n data: null;\n errors: GraphQLError[];\n}\n\nexport type GraphQLResponse<T = unknown> = GraphQLSuccessResponse<T> | GraphQLErrorResponse;\n\nexport type GraphQLOperation<R extends GraphQLResponse, V extends GraphQLVariables> = (\n variables?: V\n) => Promise<R>;\n\nexport interface ResponseParser {\n parse<T>(text: string): T;\n stringify<T>(value: T): string;\n}\n\nexport interface RequestParams {\n operationName?: string | null;\n query: string;\n variables?: Record<string, unknown> | null;\n}\n\nexport interface GraphQLRequestOptions {\n url: URL | string;\n headers?: Headers;\n parser?: ResponseParser;\n fetcher?: Fetcher;\n credentials?: Credentials;\n throwOnNonNetworkErrors?: boolean;\n}\n\nexport interface GraphQLThrowableOptions extends GraphQLRequestOptions {\n throwOnNonNetworkErrors: true;\n}\n\nexport function createGqlOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options: GraphQLThrowableOptions\n): GraphQLOperation<GraphQLSuccessResponse<R>, V>;\nexport function createGqlOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V>;\nexport function createGqlOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V> {\n return async (variables?: V): Promise<GraphQLResponse<R>> => {\n const response = await (options.fetcher ?? fetch)(options.url, {\n method: \"POST\",\n headers: ensureDefaults(options.headers, DEFAULT_HEADERS),\n credentials: options.credentials,\n body: (options.parser ?? JSON).stringify({\n operationName: getOpName(query),\n query,\n variables: variables ? clearUndefined(variables) : undefined\n } as RequestParams)\n });\n\n const rawData = await response.text();\n const parsedData = (options.parser ?? JSON).parse(rawData) as GraphQLResponse<R>;\n\n if (options.throwOnNonNetworkErrors && some(parsedData.errors) && isEmpty(parsedData.data)) {\n throw new BlockchainProviderError(parsedData.errors[0].message, { cause: parsedData.errors });\n }\n\n return parsedData;\n };\n}\n\nexport function gql(query: TemplateStringsArray): string {\n return query[0];\n}\n\nexport function getOpName(query: string): string | undefined {\n return OP_NAME_REGEX.exec(query)?.at(2);\n}\n\nexport function isRequestParam(obj: unknown): obj is GraphQLRequestOptions {\n return typeof obj === \"object\" && (obj as GraphQLRequestOptions).url !== undefined;\n}\n","const B = [\n `query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)`,\n `boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take`,\n `boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent`\n];\n\nexport const CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;\nexport const UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const HEADERS_QUERY = `query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }`;\nexport const CHECK_TX_MUTATION = `mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }`;\nexport const SEND_TX_MUTATION = `mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }`;\n"]}
|
1
|
+
{"version":3,"sources":["../src/ergo-graphql/ergoGraphQLProvider.ts","../src/utils/graphql.ts","../src/ergo-graphql/queries.ts"],"names":["ensureDefaults","isEmpty","some"],"mappings":"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","sourcesContent":["import type {\n Box,\n QueryBoxesArgs,\n Header,\n QueryBlockHeadersArgs\n} from \"@ergo-graphql/types\";\nimport {\n type Base58String,\n type BlockHeader,\n ensureDefaults,\n type HexString,\n isEmpty,\n isUndefined,\n NotSupportedError,\n orderBy,\n type SignedTransaction,\n some,\n uniq,\n uniqBy\n} from \"@fleet-sdk/common\";\nimport { ErgoAddress } from \"@fleet-sdk/core\";\nimport type {\n BoxQuery,\n BoxWhere,\n ChainProviderBox,\n HeaderQuery,\n IBlockchainProvider,\n TransactionEvaluationResult,\n TransactionReductionResult\n} from \"../types/blockchainProvider\";\nimport {\n createGqlOperation,\n type GraphQLOperation,\n type GraphQLRequestOptions,\n type GraphQLSuccessResponse,\n type GraphQLThrowableOptions,\n type GraphQLVariables,\n isRequestParam\n} from \"../utils\";\nimport {\n ALL_BOXES_QUERY,\n CHECK_TX_MUTATION,\n CONF_BOXES_QUERY,\n HEADERS_QUERY,\n SEND_TX_MUTATION,\n UNCONF_BOXES_QUERY\n} from \"./queries\";\n\nexport type GraphQLBoxWhere = BoxWhere & {\n /** Base16-encoded BoxIds */\n boxIds?: HexString[];\n\n /** Base16-encoded ErgoTrees */\n ergoTrees?: HexString[];\n\n /** Base58-encoded addresses or `ErgoAddress` objects */\n addresses?: (Base58String | ErgoAddress)[];\n};\n\nexport type GraphQLBoxQuery = BoxQuery<GraphQLBoxWhere>;\nexport type ErgoGraphQLRequestOptions = Omit<\n GraphQLRequestOptions,\n \"throwOnNonNetworkError\"\n>;\n\ntype ConfirmedBoxesResponse = { boxes: Box[] };\ntype UnconfirmedBoxesResponse = { mempool: { boxes: Box[] } };\ntype CombinedBoxesResponse = ConfirmedBoxesResponse & UnconfirmedBoxesResponse;\ntype BlockHeadersResponse = { blockHeaders: Header[] };\ntype CheckTransactionResponse = { checkTransaction: string };\ntype TransactionSubmissionResponse = { submitTransaction: string };\ntype SignedTxArgsResp = { signedTransaction: SignedTransaction };\n\nconst PAGE_SIZE = 50;\n\nexport class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {\n #options: GraphQLThrowableOptions;\n\n #getConfBoxes;\n #getUnconfBoxes;\n #getAllBoxes;\n #getHeaders;\n #checkTx;\n #sendTx;\n\n constructor(url: string | URL);\n constructor(url: ErgoGraphQLRequestOptions);\n constructor(optOrUrl: ErgoGraphQLRequestOptions | string | URL) {\n this.#options = {\n ...(isRequestParam(optOrUrl) ? optOrUrl : { url: optOrUrl }),\n throwOnNonNetworkErrors: true\n };\n\n this.#getConfBoxes = this.createOperation<\n ConfirmedBoxesResponse,\n QueryBoxesArgs\n >(CONF_BOXES_QUERY);\n\n this.#getUnconfBoxes = this.createOperation<\n UnconfirmedBoxesResponse,\n QueryBoxesArgs\n >(UNCONF_BOXES_QUERY);\n\n this.#getAllBoxes = this.createOperation<\n CombinedBoxesResponse,\n QueryBoxesArgs\n >(ALL_BOXES_QUERY);\n\n this.#getHeaders = this.createOperation<\n BlockHeadersResponse,\n QueryBlockHeadersArgs\n >(HEADERS_QUERY);\n\n this.#checkTx = this.createOperation<\n CheckTransactionResponse,\n SignedTxArgsResp\n >(CHECK_TX_MUTATION);\n\n this.#sendTx = this.createOperation<\n TransactionSubmissionResponse,\n SignedTxArgsResp\n >(SEND_TX_MUTATION);\n }\n\n #fetchBoxes(args: QueryBoxesArgs, inclConf: boolean, inclUnconf: boolean) {\n return inclConf && inclUnconf\n ? this.#getAllBoxes(args)\n : inclUnconf\n ? this.#getUnconfBoxes(args)\n : this.#getConfBoxes(args);\n }\n\n async *streamBoxes(\n query: GraphQLBoxQuery\n ): AsyncGenerator<ChainProviderBox[]> {\n if (isEmpty(query.where)) {\n throw new Error(\"Cannot fetch unspent boxes without a where clause.\");\n }\n\n const notBeingSpent = (box: Box) => !box.beingSpent;\n const returnedBoxIds = new Set<string>();\n const { where, from } = query;\n const args = buildGqlBoxQueryArgs(where);\n\n let inclChain = from !== \"mempool\";\n let inclPool = from !== \"blockchain\";\n const isMempoolAware = inclPool;\n\n do {\n const response = await this.#fetchBoxes(args, inclChain, inclPool);\n\n const { data } = response;\n let boxes: ChainProviderBox[] = [];\n\n if (inclChain && hasConfirmed(data)) {\n if (some(data.boxes)) {\n const confirmedBoxes = (\n isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes\n ).map(asConfirmed(true));\n\n boxes = boxes.concat(confirmedBoxes);\n }\n\n inclChain = data.boxes.length === PAGE_SIZE;\n }\n\n if (isMempoolAware && hasMempool(data)) {\n if (some(data.mempool.boxes)) {\n const mempoolBoxes = data.mempool.boxes\n .filter(notBeingSpent)\n .map(asConfirmed(false));\n boxes = boxes.concat(mempoolBoxes);\n }\n\n inclPool = data.mempool.boxes.length === PAGE_SIZE;\n }\n\n if (some(boxes)) {\n // boxes can be moved from the mempool to the blockchain while streaming,\n // so we need to filter out boxes that have already been returned.\n if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {\n boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId));\n }\n\n if (some(boxes)) {\n boxes = uniqBy(boxes, (box) => box.boxId);\n for (const box of boxes) returnedBoxIds.add(box.boxId);\n yield boxes;\n }\n }\n\n if (inclChain || inclPool) args.skip += PAGE_SIZE;\n } while (inclChain || inclPool);\n }\n\n async getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox[]> {\n let boxes: ChainProviderBox[] = [];\n for await (const chunk of this.streamBoxes(query)) {\n boxes = boxes.concat(chunk);\n }\n\n return orderBy(boxes, (box) => box.creationHeight);\n }\n\n async getHeaders(query: HeaderQuery): Promise<BlockHeader[]> {\n const response = await this.#getHeaders(query);\n\n return (\n response.data?.blockHeaders.map((header) => ({\n ...header,\n id: header.headerId,\n timestamp: Number(header.timestamp),\n nBits: Number(header.nBits),\n votes: header.votes.join(\"\")\n })) ?? []\n );\n }\n\n createOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options?: Partial<ErgoGraphQLRequestOptions>\n ): GraphQLOperation<GraphQLSuccessResponse<R>, V> {\n const opt = ensureDefaults(options, this.#options);\n opt.throwOnNonNetworkErrors = true;\n\n return createGqlOperation(query, opt);\n }\n\n async checkTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#checkTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.checkTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n async submitTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#sendTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.submitTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n reduceTransaction(): Promise<TransactionReductionResult> {\n throw new NotSupportedError(\n \"Transaction reducing is not supported by ergo-graphql.\"\n );\n }\n}\n\nfunction buildGqlBoxQueryArgs(where: GraphQLBoxWhere) {\n const args = {\n spent: false,\n boxIds: merge(where.boxIds, where.boxId),\n ergoTrees: merge(where.ergoTrees, where.ergoTree),\n ergoTreeTemplateHash: where.templateHash,\n tokenId: where.tokenId,\n skip: 0,\n take: PAGE_SIZE\n } satisfies QueryBoxesArgs;\n\n const addresses = merge(where.addresses, where.address);\n if (some(addresses)) {\n const trees = addresses.map((address) =>\n typeof address === \"string\"\n ? ErgoAddress.fromBase58(address).ergoTree\n : address.ergoTree\n );\n\n args.ergoTrees = uniq(\n some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees\n );\n }\n\n return args;\n}\n\nfunction merge<T>(array?: T[], el?: T) {\n if (isEmpty(array) && isUndefined(el)) return;\n\n const set = new Set<T>(array ?? []);\n if (!isUndefined(el)) set.add(el);\n return Array.from(set.values());\n}\n\nfunction hasMempool(data: unknown): data is UnconfirmedBoxesResponse {\n return !!(data as UnconfirmedBoxesResponse)?.mempool?.boxes;\n}\n\nfunction hasConfirmed(data: unknown): data is ConfirmedBoxesResponse {\n return !!(data as ConfirmedBoxesResponse)?.boxes;\n}\n\nfunction asConfirmed(confirmed: boolean) {\n return (box: Box): ChainProviderBox => ({\n ...box,\n value: BigInt(box.value),\n assets: box.assets.map((asset) => ({\n tokenId: asset.tokenId,\n amount: BigInt(asset.amount)\n })),\n confirmed\n });\n}\n","import {\n BlockchainProviderError,\n clearUndefined,\n ensureDefaults,\n isEmpty,\n some\n} from \"@fleet-sdk/common\";\n\nconst OP_NAME_REGEX = /(query|mutation)\\s?([\\w\\-_]+)?/;\nexport const DEFAULT_HEADERS: Headers = {\n \"content-type\": \"application/json; charset=utf-8\",\n accept: \"application/graphql-response+json, application/json\"\n};\n\ntype Credentials = RequestCredentials;\ntype Headers = HeadersInit;\ntype Fetcher = typeof fetch;\n\nexport type GraphQLVariables = Record<string, unknown> | null;\n\nexport interface GraphQLError {\n message: string;\n}\n\nexport interface GraphQLSuccessResponse<T = unknown> {\n data: T;\n errors: null;\n}\n\nexport interface GraphQLErrorResponse {\n data: null;\n errors: GraphQLError[];\n}\n\nexport type GraphQLResponse<T = unknown> =\n | GraphQLSuccessResponse<T>\n | GraphQLErrorResponse;\n\nexport type GraphQLOperation<\n R extends GraphQLResponse,\n V extends GraphQLVariables\n> = (variables?: V) => Promise<R>;\n\nexport interface ResponseParser {\n parse<T>(text: string): T;\n stringify<T>(value: T): string;\n}\n\nexport interface RequestParams {\n operationName?: string | null;\n query: string;\n variables?: Record<string, unknown> | null;\n}\n\nexport interface GraphQLRequestOptions {\n url: URL | string;\n headers?: Headers;\n parser?: ResponseParser;\n fetcher?: Fetcher;\n credentials?: Credentials;\n throwOnNonNetworkErrors?: boolean;\n}\n\nexport interface GraphQLThrowableOptions extends GraphQLRequestOptions {\n throwOnNonNetworkErrors: true;\n}\n\nexport function createGqlOperation<\n R,\n V extends GraphQLVariables = GraphQLVariables\n>(\n query: string,\n options: GraphQLThrowableOptions\n): GraphQLOperation<GraphQLSuccessResponse<R>, V>;\nexport function createGqlOperation<\n R,\n V extends GraphQLVariables = GraphQLVariables\n>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V>;\nexport function createGqlOperation<\n R,\n V extends GraphQLVariables = GraphQLVariables\n>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V> {\n return async (variables?: V): Promise<GraphQLResponse<R>> => {\n const response = await (options.fetcher ?? fetch)(options.url, {\n method: \"POST\",\n headers: ensureDefaults(options.headers, DEFAULT_HEADERS),\n credentials: options.credentials,\n body: (options.parser ?? JSON).stringify({\n operationName: getOpName(query),\n query,\n variables: variables ? clearUndefined(variables) : undefined\n } as RequestParams)\n });\n\n const rawData = await response.text();\n const parsedData = (options.parser ?? JSON).parse(\n rawData\n ) as GraphQLResponse<R>;\n\n if (\n options.throwOnNonNetworkErrors &&\n some(parsedData.errors) &&\n isEmpty(parsedData.data)\n ) {\n throw new BlockchainProviderError(parsedData.errors[0].message, {\n cause: parsedData.errors\n });\n }\n\n return parsedData;\n };\n}\n\nexport function gql(query: TemplateStringsArray): string {\n return query[0];\n}\n\nexport function getOpName(query: string): string | undefined {\n return OP_NAME_REGEX.exec(query)?.at(2);\n}\n\nexport function isRequestParam(obj: unknown): obj is GraphQLRequestOptions {\n return (\n typeof obj === \"object\" && (obj as GraphQLRequestOptions).url !== undefined\n );\n}\n","const B = [\n \"query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)\",\n \"boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take\",\n \"boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent\"\n];\n\nexport const CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;\nexport const UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const HEADERS_QUERY =\n \"query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }\";\nexport const CHECK_TX_MUTATION =\n \"mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }\";\nexport const SEND_TX_MUTATION =\n \"mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }\";\n"]}
|
package/dist/index.mjs
CHANGED
@@ -20,9 +20,13 @@ function createGqlOperation(query, options) {
|
|
20
20
|
})
|
21
21
|
});
|
22
22
|
const rawData = await response.text();
|
23
|
-
const parsedData = (options.parser ?? JSON).parse(
|
23
|
+
const parsedData = (options.parser ?? JSON).parse(
|
24
|
+
rawData
|
25
|
+
);
|
24
26
|
if (options.throwOnNonNetworkErrors && some(parsedData.errors) && isEmpty(parsedData.data)) {
|
25
|
-
throw new BlockchainProviderError(parsedData.errors[0].message, {
|
27
|
+
throw new BlockchainProviderError(parsedData.errors[0].message, {
|
28
|
+
cause: parsedData.errors
|
29
|
+
});
|
26
30
|
}
|
27
31
|
return parsedData;
|
28
32
|
};
|
@@ -36,16 +40,16 @@ function isRequestParam(obj) {
|
|
36
40
|
|
37
41
|
// src/ergo-graphql/queries.ts
|
38
42
|
var B = [
|
39
|
-
|
40
|
-
|
41
|
-
|
43
|
+
"query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)",
|
44
|
+
"boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take",
|
45
|
+
"boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent"
|
42
46
|
];
|
43
47
|
var CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;
|
44
48
|
var UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
45
49
|
var ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
46
|
-
var HEADERS_QUERY =
|
47
|
-
var CHECK_TX_MUTATION =
|
48
|
-
var SEND_TX_MUTATION =
|
50
|
+
var HEADERS_QUERY = "query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }";
|
51
|
+
var CHECK_TX_MUTATION = "mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }";
|
52
|
+
var SEND_TX_MUTATION = "mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }";
|
49
53
|
|
50
54
|
// src/ergo-graphql/ergoGraphQLProvider.ts
|
51
55
|
var PAGE_SIZE = 50;
|
@@ -70,13 +74,7 @@ var ErgoGraphQLProvider = class {
|
|
70
74
|
this.#sendTx = this.createOperation(SEND_TX_MUTATION);
|
71
75
|
}
|
72
76
|
#fetchBoxes(args, inclConf, inclUnconf) {
|
73
|
-
|
74
|
-
return this.#getAllBoxes(args);
|
75
|
-
} else if (inclUnconf) {
|
76
|
-
return this.#getUnconfBoxes(args);
|
77
|
-
} else {
|
78
|
-
return this.#getConfBoxes(args);
|
79
|
-
}
|
77
|
+
return inclConf && inclUnconf ? this.#getAllBoxes(args) : inclUnconf ? this.#getUnconfBoxes(args) : this.#getConfBoxes(args);
|
80
78
|
}
|
81
79
|
async *streamBoxes(query) {
|
82
80
|
if (isEmpty(query.where)) {
|
@@ -86,26 +84,26 @@ var ErgoGraphQLProvider = class {
|
|
86
84
|
const returnedBoxIds = /* @__PURE__ */ new Set();
|
87
85
|
const { where, from } = query;
|
88
86
|
const args = buildGqlBoxQueryArgs(where);
|
89
|
-
let
|
90
|
-
let
|
91
|
-
const isMempoolAware =
|
87
|
+
let inclChain = from !== "mempool";
|
88
|
+
let inclPool = from !== "blockchain";
|
89
|
+
const isMempoolAware = inclPool;
|
92
90
|
do {
|
93
|
-
const response = await this.#fetchBoxes(args,
|
91
|
+
const response = await this.#fetchBoxes(args, inclChain, inclPool);
|
94
92
|
const { data } = response;
|
95
93
|
let boxes = [];
|
96
|
-
if (
|
94
|
+
if (inclChain && hasConfirmed(data)) {
|
97
95
|
if (some(data.boxes)) {
|
98
96
|
const confirmedBoxes = (isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes).map(asConfirmed(true));
|
99
97
|
boxes = boxes.concat(confirmedBoxes);
|
100
98
|
}
|
101
|
-
|
99
|
+
inclChain = data.boxes.length === PAGE_SIZE;
|
102
100
|
}
|
103
101
|
if (isMempoolAware && hasMempool(data)) {
|
104
102
|
if (some(data.mempool.boxes)) {
|
105
103
|
const mempoolBoxes = data.mempool.boxes.filter(notBeingSpent).map(asConfirmed(false));
|
106
104
|
boxes = boxes.concat(mempoolBoxes);
|
107
105
|
}
|
108
|
-
|
106
|
+
inclPool = data.mempool.boxes.length === PAGE_SIZE;
|
109
107
|
}
|
110
108
|
if (some(boxes)) {
|
111
109
|
if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {
|
@@ -113,13 +111,12 @@ var ErgoGraphQLProvider = class {
|
|
113
111
|
}
|
114
112
|
if (some(boxes)) {
|
115
113
|
boxes = uniqBy(boxes, (box) => box.boxId);
|
116
|
-
|
114
|
+
for (const box of boxes) returnedBoxIds.add(box.boxId);
|
117
115
|
yield boxes;
|
118
116
|
}
|
119
117
|
}
|
120
|
-
if (
|
121
|
-
|
122
|
-
} while (fetchFromChain || fetchFromMempool);
|
118
|
+
if (inclChain || inclPool) args.skip += PAGE_SIZE;
|
119
|
+
} while (inclChain || inclPool);
|
123
120
|
}
|
124
121
|
async getBoxes(query) {
|
125
122
|
let boxes = [];
|
@@ -160,7 +157,9 @@ var ErgoGraphQLProvider = class {
|
|
160
157
|
}
|
161
158
|
}
|
162
159
|
reduceTransaction() {
|
163
|
-
throw new NotSupportedError(
|
160
|
+
throw new NotSupportedError(
|
161
|
+
"Transaction reducing is not supported by ergo-graphql."
|
162
|
+
);
|
164
163
|
}
|
165
164
|
};
|
166
165
|
function buildGqlBoxQueryArgs(where) {
|
@@ -178,16 +177,16 @@ function buildGqlBoxQueryArgs(where) {
|
|
178
177
|
const trees = addresses.map(
|
179
178
|
(address) => typeof address === "string" ? ErgoAddress.fromBase58(address).ergoTree : address.ergoTree
|
180
179
|
);
|
181
|
-
args.ergoTrees = uniq(
|
180
|
+
args.ergoTrees = uniq(
|
181
|
+
some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees
|
182
|
+
);
|
182
183
|
}
|
183
184
|
return args;
|
184
185
|
}
|
185
186
|
function merge(array, el) {
|
186
|
-
if (isEmpty(array) && isUndefined(el))
|
187
|
-
return;
|
187
|
+
if (isEmpty(array) && isUndefined(el)) return;
|
188
188
|
const set = new Set(array ?? []);
|
189
|
-
if (!isUndefined(el))
|
190
|
-
set.add(el);
|
189
|
+
if (!isUndefined(el)) set.add(el);
|
191
190
|
return Array.from(set.values());
|
192
191
|
}
|
193
192
|
function hasMempool(data) {
|
package/dist/index.mjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/ergo-graphql/ergoGraphQLProvider.ts","../src/utils/graphql.ts","../src/ergo-graphql/queries.ts"],"names":["ensureDefaults","isEmpty","some"],"mappings":"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","sourcesContent":["import {\n Box,\n QueryBoxesArgs as BoxesArgs,\n Header,\n QueryBlockHeadersArgs as HeadersArgs\n} from \"@ergo-graphql/types\";\nimport {\n Base58String,\n BlockHeader,\n ensureDefaults,\n HexString,\n isEmpty,\n isUndefined,\n NotSupportedError,\n orderBy,\n SignedTransaction,\n some,\n uniq,\n uniqBy\n} from \"@fleet-sdk/common\";\nimport { ErgoAddress } from \"@fleet-sdk/core\";\nimport {\n BoxQuery,\n BoxWhere,\n ChainProviderBox,\n HeaderQuery,\n IBlockchainProvider,\n TransactionEvaluationResult,\n TransactionReductionResult\n} from \"../types\";\nimport {\n createGqlOperation,\n GraphQLOperation,\n GraphQLRequestOptions,\n GraphQLSuccessResponse,\n GraphQLThrowableOptions,\n GraphQLVariables,\n isRequestParam\n} from \"../utils\";\nimport {\n ALL_BOXES_QUERY,\n CHECK_TX_MUTATION,\n CONF_BOXES_QUERY,\n HEADERS_QUERY,\n SEND_TX_MUTATION,\n UNCONF_BOXES_QUERY\n} from \"./queries\";\n\nexport type GraphQLBoxWhere = BoxWhere & {\n /** Base16-encoded BoxIds */\n boxIds?: HexString[];\n\n /** Base16-encoded ErgoTrees */\n ergoTrees?: HexString[];\n\n /** Base58-encoded addresses or `ErgoAddress` objects */\n addresses?: (Base58String | ErgoAddress)[];\n};\n\nexport type GraphQLBoxQuery = BoxQuery<GraphQLBoxWhere>;\nexport type ErgoGraphQLRequestOptions = Omit<GraphQLRequestOptions, \"throwOnNonNetworkError\">;\n\ntype ConfBoxesResp = { boxes: Box[] };\ntype UnconfBoxesResp = { mempool: { boxes: Box[] } };\ntype AllBoxesResp = ConfBoxesResp & UnconfBoxesResp;\ntype HeadersResp = { blockHeaders: Header[] };\ntype CheckTxResp = { checkTransaction: string };\ntype SendTxResp = { submitTransaction: string };\ntype SignedTxArgsResp = { signedTransaction: SignedTransaction };\n\nconst PAGE_SIZE = 50;\n\nexport class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {\n #options: GraphQLThrowableOptions;\n\n #getConfBoxes;\n #getUnconfBoxes;\n #getAllBoxes;\n #getHeaders;\n #checkTx;\n #sendTx;\n\n constructor(url: string | URL);\n constructor(url: ErgoGraphQLRequestOptions);\n constructor(optOrUrl: ErgoGraphQLRequestOptions | string | URL) {\n this.#options = {\n ...(isRequestParam(optOrUrl) ? optOrUrl : { url: optOrUrl }),\n throwOnNonNetworkErrors: true\n };\n\n this.#getConfBoxes = this.createOperation<ConfBoxesResp, BoxesArgs>(CONF_BOXES_QUERY);\n this.#getUnconfBoxes = this.createOperation<UnconfBoxesResp, BoxesArgs>(UNCONF_BOXES_QUERY);\n this.#getAllBoxes = this.createOperation<AllBoxesResp, BoxesArgs>(ALL_BOXES_QUERY);\n this.#getHeaders = this.createOperation<HeadersResp, HeadersArgs>(HEADERS_QUERY);\n this.#checkTx = this.createOperation<CheckTxResp, SignedTxArgsResp>(CHECK_TX_MUTATION);\n this.#sendTx = this.createOperation<SendTxResp, SignedTxArgsResp>(SEND_TX_MUTATION);\n }\n\n #fetchBoxes(args: BoxesArgs, inclConf: boolean, inclUnconf: boolean) {\n if (inclConf && inclUnconf) {\n return this.#getAllBoxes(args);\n } else if (inclUnconf) {\n return this.#getUnconfBoxes(args);\n } else {\n return this.#getConfBoxes(args);\n }\n }\n\n async *streamBoxes(query: GraphQLBoxQuery): AsyncGenerator<ChainProviderBox[]> {\n if (isEmpty(query.where)) {\n throw new Error(\"Cannot fetch unspent boxes without a where clause.\");\n }\n\n const notBeingSpent = (box: Box) => !box.beingSpent;\n const returnedBoxIds = new Set<string>();\n const { where, from } = query;\n const args = buildGqlBoxQueryArgs(where);\n\n let fetchFromChain = from !== \"mempool\";\n let fetchFromMempool = from !== \"blockchain\";\n const isMempoolAware = fetchFromMempool;\n\n do {\n const response = await this.#fetchBoxes(args, fetchFromChain, fetchFromMempool);\n\n const { data } = response;\n let boxes: ChainProviderBox[] = [];\n\n if (fetchFromChain && hasConfirmed(data)) {\n if (some(data.boxes)) {\n const confirmedBoxes = (\n isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes\n ).map(asConfirmed(true));\n\n boxes = boxes.concat(confirmedBoxes);\n }\n\n fetchFromChain = data.boxes.length === PAGE_SIZE;\n }\n\n if (isMempoolAware && hasMempool(data)) {\n if (some(data.mempool.boxes)) {\n const mempoolBoxes = data.mempool.boxes.filter(notBeingSpent).map(asConfirmed(false));\n boxes = boxes.concat(mempoolBoxes);\n }\n\n fetchFromMempool = data.mempool.boxes.length === PAGE_SIZE;\n }\n\n if (some(boxes)) {\n // boxes can be moved from the mempool to the blockchain while streaming,\n // so we need to filter out boxes that have already been returned.\n if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {\n boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId));\n }\n\n if (some(boxes)) {\n boxes = uniqBy(boxes, (box) => box.boxId);\n boxes.forEach((box) => returnedBoxIds.add(box.boxId));\n\n yield boxes;\n }\n }\n\n if (fetchFromChain || fetchFromMempool) args.skip += PAGE_SIZE;\n } while (fetchFromChain || fetchFromMempool);\n }\n\n async getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox[]> {\n let boxes: ChainProviderBox[] = [];\n for await (const chunk of this.streamBoxes(query)) {\n boxes = boxes.concat(chunk);\n }\n\n return orderBy(boxes, (box) => box.creationHeight);\n }\n\n async getHeaders(query: HeaderQuery): Promise<BlockHeader[]> {\n const response = await this.#getHeaders(query);\n\n return (\n response.data?.blockHeaders.map((header) => ({\n ...header,\n id: header.headerId,\n timestamp: Number(header.timestamp),\n nBits: Number(header.nBits),\n votes: header.votes.join(\"\")\n })) ?? []\n );\n }\n\n createOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options?: Partial<ErgoGraphQLRequestOptions>\n ): GraphQLOperation<GraphQLSuccessResponse<R>, V> {\n const opt = ensureDefaults(options, this.#options);\n opt.throwOnNonNetworkErrors = true;\n\n return createGqlOperation(query, opt);\n }\n\n async checkTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#checkTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.checkTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n async submitTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#sendTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.submitTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n reduceTransaction(): Promise<TransactionReductionResult> {\n throw new NotSupportedError(\"Transaction reducing is not supported by ergo-graphql.\");\n }\n}\n\nfunction buildGqlBoxQueryArgs(where: GraphQLBoxWhere) {\n const args = {\n spent: false,\n boxIds: merge(where.boxIds, where.boxId),\n ergoTrees: merge(where.ergoTrees, where.ergoTree),\n ergoTreeTemplateHash: where.templateHash,\n tokenId: where.tokenId,\n skip: 0,\n take: PAGE_SIZE\n } satisfies BoxesArgs;\n\n const addresses = merge(where.addresses, where.address);\n if (some(addresses)) {\n const trees = addresses.map((address) =>\n typeof address === \"string\" ? ErgoAddress.fromBase58(address).ergoTree : address.ergoTree\n );\n\n args.ergoTrees = uniq(some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees);\n }\n\n return args;\n}\n\nfunction merge<T>(array?: T[], el?: T) {\n if (isEmpty(array) && isUndefined(el)) return;\n\n const set = new Set<T>(array ?? []);\n if (!isUndefined(el)) set.add(el);\n return Array.from(set.values());\n}\n\nfunction hasMempool(data: AllBoxesResp | ConfBoxesResp | UnconfBoxesResp): data is UnconfBoxesResp {\n return !!(data as UnconfBoxesResp)?.mempool?.boxes;\n}\n\nfunction hasConfirmed(data: AllBoxesResp | ConfBoxesResp | UnconfBoxesResp): data is ConfBoxesResp {\n return !!(data as ConfBoxesResp)?.boxes;\n}\n\nfunction asConfirmed(confirmed: boolean) {\n return (box: Box): ChainProviderBox => ({\n ...box,\n value: BigInt(box.value),\n assets: box.assets.map((asset) => ({\n tokenId: asset.tokenId,\n amount: BigInt(asset.amount)\n })),\n confirmed\n });\n}\n","import {\n BlockchainProviderError,\n clearUndefined,\n ensureDefaults,\n isEmpty,\n some\n} from \"@fleet-sdk/common\";\n\nconst OP_NAME_REGEX = /(query|mutation)\\s?([\\w\\-_]+)?/;\nexport const DEFAULT_HEADERS: Headers = {\n \"content-type\": \"application/json; charset=utf-8\",\n accept: \"application/graphql-response+json, application/json\"\n};\n\ntype Credentials = RequestCredentials;\ntype Headers = HeadersInit;\ntype Fetcher = typeof fetch;\n\nexport type GraphQLVariables = Record<string, unknown> | null;\n\nexport interface GraphQLError {\n message: string;\n}\n\nexport interface GraphQLSuccessResponse<T = unknown> {\n data: T;\n errors: null;\n}\n\nexport interface GraphQLErrorResponse {\n data: null;\n errors: GraphQLError[];\n}\n\nexport type GraphQLResponse<T = unknown> = GraphQLSuccessResponse<T> | GraphQLErrorResponse;\n\nexport type GraphQLOperation<R extends GraphQLResponse, V extends GraphQLVariables> = (\n variables?: V\n) => Promise<R>;\n\nexport interface ResponseParser {\n parse<T>(text: string): T;\n stringify<T>(value: T): string;\n}\n\nexport interface RequestParams {\n operationName?: string | null;\n query: string;\n variables?: Record<string, unknown> | null;\n}\n\nexport interface GraphQLRequestOptions {\n url: URL | string;\n headers?: Headers;\n parser?: ResponseParser;\n fetcher?: Fetcher;\n credentials?: Credentials;\n throwOnNonNetworkErrors?: boolean;\n}\n\nexport interface GraphQLThrowableOptions extends GraphQLRequestOptions {\n throwOnNonNetworkErrors: true;\n}\n\nexport function createGqlOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options: GraphQLThrowableOptions\n): GraphQLOperation<GraphQLSuccessResponse<R>, V>;\nexport function createGqlOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V>;\nexport function createGqlOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V> {\n return async (variables?: V): Promise<GraphQLResponse<R>> => {\n const response = await (options.fetcher ?? fetch)(options.url, {\n method: \"POST\",\n headers: ensureDefaults(options.headers, DEFAULT_HEADERS),\n credentials: options.credentials,\n body: (options.parser ?? JSON).stringify({\n operationName: getOpName(query),\n query,\n variables: variables ? clearUndefined(variables) : undefined\n } as RequestParams)\n });\n\n const rawData = await response.text();\n const parsedData = (options.parser ?? JSON).parse(rawData) as GraphQLResponse<R>;\n\n if (options.throwOnNonNetworkErrors && some(parsedData.errors) && isEmpty(parsedData.data)) {\n throw new BlockchainProviderError(parsedData.errors[0].message, { cause: parsedData.errors });\n }\n\n return parsedData;\n };\n}\n\nexport function gql(query: TemplateStringsArray): string {\n return query[0];\n}\n\nexport function getOpName(query: string): string | undefined {\n return OP_NAME_REGEX.exec(query)?.at(2);\n}\n\nexport function isRequestParam(obj: unknown): obj is GraphQLRequestOptions {\n return typeof obj === \"object\" && (obj as GraphQLRequestOptions).url !== undefined;\n}\n","const B = [\n `query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)`,\n `boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take`,\n `boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent`\n];\n\nexport const CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;\nexport const UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const HEADERS_QUERY = `query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }`;\nexport const CHECK_TX_MUTATION = `mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }`;\nexport const SEND_TX_MUTATION = `mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }`;\n"]}
|
1
|
+
{"version":3,"sources":["../src/ergo-graphql/ergoGraphQLProvider.ts","../src/utils/graphql.ts","../src/ergo-graphql/queries.ts"],"names":["ensureDefaults","isEmpty","some"],"mappings":"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","sourcesContent":["import type {\n Box,\n QueryBoxesArgs,\n Header,\n QueryBlockHeadersArgs\n} from \"@ergo-graphql/types\";\nimport {\n type Base58String,\n type BlockHeader,\n ensureDefaults,\n type HexString,\n isEmpty,\n isUndefined,\n NotSupportedError,\n orderBy,\n type SignedTransaction,\n some,\n uniq,\n uniqBy\n} from \"@fleet-sdk/common\";\nimport { ErgoAddress } from \"@fleet-sdk/core\";\nimport type {\n BoxQuery,\n BoxWhere,\n ChainProviderBox,\n HeaderQuery,\n IBlockchainProvider,\n TransactionEvaluationResult,\n TransactionReductionResult\n} from \"../types/blockchainProvider\";\nimport {\n createGqlOperation,\n type GraphQLOperation,\n type GraphQLRequestOptions,\n type GraphQLSuccessResponse,\n type GraphQLThrowableOptions,\n type GraphQLVariables,\n isRequestParam\n} from \"../utils\";\nimport {\n ALL_BOXES_QUERY,\n CHECK_TX_MUTATION,\n CONF_BOXES_QUERY,\n HEADERS_QUERY,\n SEND_TX_MUTATION,\n UNCONF_BOXES_QUERY\n} from \"./queries\";\n\nexport type GraphQLBoxWhere = BoxWhere & {\n /** Base16-encoded BoxIds */\n boxIds?: HexString[];\n\n /** Base16-encoded ErgoTrees */\n ergoTrees?: HexString[];\n\n /** Base58-encoded addresses or `ErgoAddress` objects */\n addresses?: (Base58String | ErgoAddress)[];\n};\n\nexport type GraphQLBoxQuery = BoxQuery<GraphQLBoxWhere>;\nexport type ErgoGraphQLRequestOptions = Omit<\n GraphQLRequestOptions,\n \"throwOnNonNetworkError\"\n>;\n\ntype ConfirmedBoxesResponse = { boxes: Box[] };\ntype UnconfirmedBoxesResponse = { mempool: { boxes: Box[] } };\ntype CombinedBoxesResponse = ConfirmedBoxesResponse & UnconfirmedBoxesResponse;\ntype BlockHeadersResponse = { blockHeaders: Header[] };\ntype CheckTransactionResponse = { checkTransaction: string };\ntype TransactionSubmissionResponse = { submitTransaction: string };\ntype SignedTxArgsResp = { signedTransaction: SignedTransaction };\n\nconst PAGE_SIZE = 50;\n\nexport class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {\n #options: GraphQLThrowableOptions;\n\n #getConfBoxes;\n #getUnconfBoxes;\n #getAllBoxes;\n #getHeaders;\n #checkTx;\n #sendTx;\n\n constructor(url: string | URL);\n constructor(url: ErgoGraphQLRequestOptions);\n constructor(optOrUrl: ErgoGraphQLRequestOptions | string | URL) {\n this.#options = {\n ...(isRequestParam(optOrUrl) ? optOrUrl : { url: optOrUrl }),\n throwOnNonNetworkErrors: true\n };\n\n this.#getConfBoxes = this.createOperation<\n ConfirmedBoxesResponse,\n QueryBoxesArgs\n >(CONF_BOXES_QUERY);\n\n this.#getUnconfBoxes = this.createOperation<\n UnconfirmedBoxesResponse,\n QueryBoxesArgs\n >(UNCONF_BOXES_QUERY);\n\n this.#getAllBoxes = this.createOperation<\n CombinedBoxesResponse,\n QueryBoxesArgs\n >(ALL_BOXES_QUERY);\n\n this.#getHeaders = this.createOperation<\n BlockHeadersResponse,\n QueryBlockHeadersArgs\n >(HEADERS_QUERY);\n\n this.#checkTx = this.createOperation<\n CheckTransactionResponse,\n SignedTxArgsResp\n >(CHECK_TX_MUTATION);\n\n this.#sendTx = this.createOperation<\n TransactionSubmissionResponse,\n SignedTxArgsResp\n >(SEND_TX_MUTATION);\n }\n\n #fetchBoxes(args: QueryBoxesArgs, inclConf: boolean, inclUnconf: boolean) {\n return inclConf && inclUnconf\n ? this.#getAllBoxes(args)\n : inclUnconf\n ? this.#getUnconfBoxes(args)\n : this.#getConfBoxes(args);\n }\n\n async *streamBoxes(\n query: GraphQLBoxQuery\n ): AsyncGenerator<ChainProviderBox[]> {\n if (isEmpty(query.where)) {\n throw new Error(\"Cannot fetch unspent boxes without a where clause.\");\n }\n\n const notBeingSpent = (box: Box) => !box.beingSpent;\n const returnedBoxIds = new Set<string>();\n const { where, from } = query;\n const args = buildGqlBoxQueryArgs(where);\n\n let inclChain = from !== \"mempool\";\n let inclPool = from !== \"blockchain\";\n const isMempoolAware = inclPool;\n\n do {\n const response = await this.#fetchBoxes(args, inclChain, inclPool);\n\n const { data } = response;\n let boxes: ChainProviderBox[] = [];\n\n if (inclChain && hasConfirmed(data)) {\n if (some(data.boxes)) {\n const confirmedBoxes = (\n isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes\n ).map(asConfirmed(true));\n\n boxes = boxes.concat(confirmedBoxes);\n }\n\n inclChain = data.boxes.length === PAGE_SIZE;\n }\n\n if (isMempoolAware && hasMempool(data)) {\n if (some(data.mempool.boxes)) {\n const mempoolBoxes = data.mempool.boxes\n .filter(notBeingSpent)\n .map(asConfirmed(false));\n boxes = boxes.concat(mempoolBoxes);\n }\n\n inclPool = data.mempool.boxes.length === PAGE_SIZE;\n }\n\n if (some(boxes)) {\n // boxes can be moved from the mempool to the blockchain while streaming,\n // so we need to filter out boxes that have already been returned.\n if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {\n boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId));\n }\n\n if (some(boxes)) {\n boxes = uniqBy(boxes, (box) => box.boxId);\n for (const box of boxes) returnedBoxIds.add(box.boxId);\n yield boxes;\n }\n }\n\n if (inclChain || inclPool) args.skip += PAGE_SIZE;\n } while (inclChain || inclPool);\n }\n\n async getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox[]> {\n let boxes: ChainProviderBox[] = [];\n for await (const chunk of this.streamBoxes(query)) {\n boxes = boxes.concat(chunk);\n }\n\n return orderBy(boxes, (box) => box.creationHeight);\n }\n\n async getHeaders(query: HeaderQuery): Promise<BlockHeader[]> {\n const response = await this.#getHeaders(query);\n\n return (\n response.data?.blockHeaders.map((header) => ({\n ...header,\n id: header.headerId,\n timestamp: Number(header.timestamp),\n nBits: Number(header.nBits),\n votes: header.votes.join(\"\")\n })) ?? []\n );\n }\n\n createOperation<R, V extends GraphQLVariables = GraphQLVariables>(\n query: string,\n options?: Partial<ErgoGraphQLRequestOptions>\n ): GraphQLOperation<GraphQLSuccessResponse<R>, V> {\n const opt = ensureDefaults(options, this.#options);\n opt.throwOnNonNetworkErrors = true;\n\n return createGqlOperation(query, opt);\n }\n\n async checkTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#checkTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.checkTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n async submitTransaction(\n signedTransaction: SignedTransaction\n ): Promise<TransactionEvaluationResult> {\n try {\n const response = await this.#sendTx({ signedTransaction });\n\n return { success: true, transactionId: response.data.submitTransaction };\n } catch (e) {\n return { success: false, message: (e as Error).message };\n }\n }\n\n reduceTransaction(): Promise<TransactionReductionResult> {\n throw new NotSupportedError(\n \"Transaction reducing is not supported by ergo-graphql.\"\n );\n }\n}\n\nfunction buildGqlBoxQueryArgs(where: GraphQLBoxWhere) {\n const args = {\n spent: false,\n boxIds: merge(where.boxIds, where.boxId),\n ergoTrees: merge(where.ergoTrees, where.ergoTree),\n ergoTreeTemplateHash: where.templateHash,\n tokenId: where.tokenId,\n skip: 0,\n take: PAGE_SIZE\n } satisfies QueryBoxesArgs;\n\n const addresses = merge(where.addresses, where.address);\n if (some(addresses)) {\n const trees = addresses.map((address) =>\n typeof address === \"string\"\n ? ErgoAddress.fromBase58(address).ergoTree\n : address.ergoTree\n );\n\n args.ergoTrees = uniq(\n some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees\n );\n }\n\n return args;\n}\n\nfunction merge<T>(array?: T[], el?: T) {\n if (isEmpty(array) && isUndefined(el)) return;\n\n const set = new Set<T>(array ?? []);\n if (!isUndefined(el)) set.add(el);\n return Array.from(set.values());\n}\n\nfunction hasMempool(data: unknown): data is UnconfirmedBoxesResponse {\n return !!(data as UnconfirmedBoxesResponse)?.mempool?.boxes;\n}\n\nfunction hasConfirmed(data: unknown): data is ConfirmedBoxesResponse {\n return !!(data as ConfirmedBoxesResponse)?.boxes;\n}\n\nfunction asConfirmed(confirmed: boolean) {\n return (box: Box): ChainProviderBox => ({\n ...box,\n value: BigInt(box.value),\n assets: box.assets.map((asset) => ({\n tokenId: asset.tokenId,\n amount: BigInt(asset.amount)\n })),\n confirmed\n });\n}\n","import {\n BlockchainProviderError,\n clearUndefined,\n ensureDefaults,\n isEmpty,\n some\n} from \"@fleet-sdk/common\";\n\nconst OP_NAME_REGEX = /(query|mutation)\\s?([\\w\\-_]+)?/;\nexport const DEFAULT_HEADERS: Headers = {\n \"content-type\": \"application/json; charset=utf-8\",\n accept: \"application/graphql-response+json, application/json\"\n};\n\ntype Credentials = RequestCredentials;\ntype Headers = HeadersInit;\ntype Fetcher = typeof fetch;\n\nexport type GraphQLVariables = Record<string, unknown> | null;\n\nexport interface GraphQLError {\n message: string;\n}\n\nexport interface GraphQLSuccessResponse<T = unknown> {\n data: T;\n errors: null;\n}\n\nexport interface GraphQLErrorResponse {\n data: null;\n errors: GraphQLError[];\n}\n\nexport type GraphQLResponse<T = unknown> =\n | GraphQLSuccessResponse<T>\n | GraphQLErrorResponse;\n\nexport type GraphQLOperation<\n R extends GraphQLResponse,\n V extends GraphQLVariables\n> = (variables?: V) => Promise<R>;\n\nexport interface ResponseParser {\n parse<T>(text: string): T;\n stringify<T>(value: T): string;\n}\n\nexport interface RequestParams {\n operationName?: string | null;\n query: string;\n variables?: Record<string, unknown> | null;\n}\n\nexport interface GraphQLRequestOptions {\n url: URL | string;\n headers?: Headers;\n parser?: ResponseParser;\n fetcher?: Fetcher;\n credentials?: Credentials;\n throwOnNonNetworkErrors?: boolean;\n}\n\nexport interface GraphQLThrowableOptions extends GraphQLRequestOptions {\n throwOnNonNetworkErrors: true;\n}\n\nexport function createGqlOperation<\n R,\n V extends GraphQLVariables = GraphQLVariables\n>(\n query: string,\n options: GraphQLThrowableOptions\n): GraphQLOperation<GraphQLSuccessResponse<R>, V>;\nexport function createGqlOperation<\n R,\n V extends GraphQLVariables = GraphQLVariables\n>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V>;\nexport function createGqlOperation<\n R,\n V extends GraphQLVariables = GraphQLVariables\n>(\n query: string,\n options: GraphQLRequestOptions\n): GraphQLOperation<GraphQLResponse<R>, V> {\n return async (variables?: V): Promise<GraphQLResponse<R>> => {\n const response = await (options.fetcher ?? fetch)(options.url, {\n method: \"POST\",\n headers: ensureDefaults(options.headers, DEFAULT_HEADERS),\n credentials: options.credentials,\n body: (options.parser ?? JSON).stringify({\n operationName: getOpName(query),\n query,\n variables: variables ? clearUndefined(variables) : undefined\n } as RequestParams)\n });\n\n const rawData = await response.text();\n const parsedData = (options.parser ?? JSON).parse(\n rawData\n ) as GraphQLResponse<R>;\n\n if (\n options.throwOnNonNetworkErrors &&\n some(parsedData.errors) &&\n isEmpty(parsedData.data)\n ) {\n throw new BlockchainProviderError(parsedData.errors[0].message, {\n cause: parsedData.errors\n });\n }\n\n return parsedData;\n };\n}\n\nexport function gql(query: TemplateStringsArray): string {\n return query[0];\n}\n\nexport function getOpName(query: string): string | undefined {\n return OP_NAME_REGEX.exec(query)?.at(2);\n}\n\nexport function isRequestParam(obj: unknown): obj is GraphQLRequestOptions {\n return (\n typeof obj === \"object\" && (obj as GraphQLRequestOptions).url !== undefined\n );\n}\n","const B = [\n \"query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)\",\n \"boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take\",\n \"boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent\"\n];\n\nexport const CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;\nexport const UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;\nexport const HEADERS_QUERY =\n \"query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }\";\nexport const CHECK_TX_MUTATION =\n \"mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }\";\nexport const SEND_TX_MUTATION =\n \"mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }\";\n"]}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@fleet-sdk/blockchain-providers",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.5.0",
|
4
4
|
"description": "Blockchain data providers",
|
5
5
|
"main": "./dist/index.js",
|
6
6
|
"module": "./dist/index.mjs",
|
@@ -30,11 +30,11 @@
|
|
30
30
|
"crypto"
|
31
31
|
],
|
32
32
|
"engines": {
|
33
|
-
"node": ">=
|
33
|
+
"node": ">=18"
|
34
34
|
},
|
35
35
|
"dependencies": {
|
36
|
-
"@fleet-sdk/common": "^0.
|
37
|
-
"@fleet-sdk/core": "^0.
|
36
|
+
"@fleet-sdk/common": "^0.4.1",
|
37
|
+
"@fleet-sdk/core": "^0.5.0"
|
38
38
|
},
|
39
39
|
"files": [
|
40
40
|
"src",
|
@@ -1,25 +1,25 @@
|
|
1
|
-
import {
|
1
|
+
import type {
|
2
2
|
Box,
|
3
|
-
QueryBoxesArgs
|
3
|
+
QueryBoxesArgs,
|
4
4
|
Header,
|
5
|
-
QueryBlockHeadersArgs
|
5
|
+
QueryBlockHeadersArgs
|
6
6
|
} from "@ergo-graphql/types";
|
7
7
|
import {
|
8
|
-
Base58String,
|
9
|
-
BlockHeader,
|
8
|
+
type Base58String,
|
9
|
+
type BlockHeader,
|
10
10
|
ensureDefaults,
|
11
|
-
HexString,
|
11
|
+
type HexString,
|
12
12
|
isEmpty,
|
13
13
|
isUndefined,
|
14
14
|
NotSupportedError,
|
15
15
|
orderBy,
|
16
|
-
SignedTransaction,
|
16
|
+
type SignedTransaction,
|
17
17
|
some,
|
18
18
|
uniq,
|
19
19
|
uniqBy
|
20
20
|
} from "@fleet-sdk/common";
|
21
21
|
import { ErgoAddress } from "@fleet-sdk/core";
|
22
|
-
import {
|
22
|
+
import type {
|
23
23
|
BoxQuery,
|
24
24
|
BoxWhere,
|
25
25
|
ChainProviderBox,
|
@@ -27,14 +27,14 @@ import {
|
|
27
27
|
IBlockchainProvider,
|
28
28
|
TransactionEvaluationResult,
|
29
29
|
TransactionReductionResult
|
30
|
-
} from "../types";
|
30
|
+
} from "../types/blockchainProvider";
|
31
31
|
import {
|
32
32
|
createGqlOperation,
|
33
|
-
GraphQLOperation,
|
34
|
-
GraphQLRequestOptions,
|
35
|
-
GraphQLSuccessResponse,
|
36
|
-
GraphQLThrowableOptions,
|
37
|
-
GraphQLVariables,
|
33
|
+
type GraphQLOperation,
|
34
|
+
type GraphQLRequestOptions,
|
35
|
+
type GraphQLSuccessResponse,
|
36
|
+
type GraphQLThrowableOptions,
|
37
|
+
type GraphQLVariables,
|
38
38
|
isRequestParam
|
39
39
|
} from "../utils";
|
40
40
|
import {
|
@@ -58,14 +58,17 @@ export type GraphQLBoxWhere = BoxWhere & {
|
|
58
58
|
};
|
59
59
|
|
60
60
|
export type GraphQLBoxQuery = BoxQuery<GraphQLBoxWhere>;
|
61
|
-
export type ErgoGraphQLRequestOptions = Omit<
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
type
|
67
|
-
type
|
68
|
-
type
|
61
|
+
export type ErgoGraphQLRequestOptions = Omit<
|
62
|
+
GraphQLRequestOptions,
|
63
|
+
"throwOnNonNetworkError"
|
64
|
+
>;
|
65
|
+
|
66
|
+
type ConfirmedBoxesResponse = { boxes: Box[] };
|
67
|
+
type UnconfirmedBoxesResponse = { mempool: { boxes: Box[] } };
|
68
|
+
type CombinedBoxesResponse = ConfirmedBoxesResponse & UnconfirmedBoxesResponse;
|
69
|
+
type BlockHeadersResponse = { blockHeaders: Header[] };
|
70
|
+
type CheckTransactionResponse = { checkTransaction: string };
|
71
|
+
type TransactionSubmissionResponse = { submitTransaction: string };
|
69
72
|
type SignedTxArgsResp = { signedTransaction: SignedTransaction };
|
70
73
|
|
71
74
|
const PAGE_SIZE = 50;
|
@@ -88,25 +91,48 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
|
88
91
|
throwOnNonNetworkErrors: true
|
89
92
|
};
|
90
93
|
|
91
|
-
this.#getConfBoxes = this.createOperation<
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
this.#
|
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);
|
97
123
|
}
|
98
124
|
|
99
|
-
#fetchBoxes(args:
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
return this.#getConfBoxes(args);
|
106
|
-
}
|
125
|
+
#fetchBoxes(args: QueryBoxesArgs, inclConf: boolean, inclUnconf: boolean) {
|
126
|
+
return inclConf && inclUnconf
|
127
|
+
? this.#getAllBoxes(args)
|
128
|
+
: inclUnconf
|
129
|
+
? this.#getUnconfBoxes(args)
|
130
|
+
: this.#getConfBoxes(args);
|
107
131
|
}
|
108
132
|
|
109
|
-
async *streamBoxes(
|
133
|
+
async *streamBoxes(
|
134
|
+
query: GraphQLBoxQuery
|
135
|
+
): AsyncGenerator<ChainProviderBox[]> {
|
110
136
|
if (isEmpty(query.where)) {
|
111
137
|
throw new Error("Cannot fetch unspent boxes without a where clause.");
|
112
138
|
}
|
@@ -116,17 +142,17 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
|
116
142
|
const { where, from } = query;
|
117
143
|
const args = buildGqlBoxQueryArgs(where);
|
118
144
|
|
119
|
-
let
|
120
|
-
let
|
121
|
-
const isMempoolAware =
|
145
|
+
let inclChain = from !== "mempool";
|
146
|
+
let inclPool = from !== "blockchain";
|
147
|
+
const isMempoolAware = inclPool;
|
122
148
|
|
123
149
|
do {
|
124
|
-
const response = await this.#fetchBoxes(args,
|
150
|
+
const response = await this.#fetchBoxes(args, inclChain, inclPool);
|
125
151
|
|
126
152
|
const { data } = response;
|
127
153
|
let boxes: ChainProviderBox[] = [];
|
128
154
|
|
129
|
-
if (
|
155
|
+
if (inclChain && hasConfirmed(data)) {
|
130
156
|
if (some(data.boxes)) {
|
131
157
|
const confirmedBoxes = (
|
132
158
|
isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes
|
@@ -135,16 +161,18 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
|
135
161
|
boxes = boxes.concat(confirmedBoxes);
|
136
162
|
}
|
137
163
|
|
138
|
-
|
164
|
+
inclChain = data.boxes.length === PAGE_SIZE;
|
139
165
|
}
|
140
166
|
|
141
167
|
if (isMempoolAware && hasMempool(data)) {
|
142
168
|
if (some(data.mempool.boxes)) {
|
143
|
-
const mempoolBoxes = data.mempool.boxes
|
169
|
+
const mempoolBoxes = data.mempool.boxes
|
170
|
+
.filter(notBeingSpent)
|
171
|
+
.map(asConfirmed(false));
|
144
172
|
boxes = boxes.concat(mempoolBoxes);
|
145
173
|
}
|
146
174
|
|
147
|
-
|
175
|
+
inclPool = data.mempool.boxes.length === PAGE_SIZE;
|
148
176
|
}
|
149
177
|
|
150
178
|
if (some(boxes)) {
|
@@ -156,14 +184,13 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
|
156
184
|
|
157
185
|
if (some(boxes)) {
|
158
186
|
boxes = uniqBy(boxes, (box) => box.boxId);
|
159
|
-
|
160
|
-
|
187
|
+
for (const box of boxes) returnedBoxIds.add(box.boxId);
|
161
188
|
yield boxes;
|
162
189
|
}
|
163
190
|
}
|
164
191
|
|
165
|
-
if (
|
166
|
-
} while (
|
192
|
+
if (inclChain || inclPool) args.skip += PAGE_SIZE;
|
193
|
+
} while (inclChain || inclPool);
|
167
194
|
}
|
168
195
|
|
169
196
|
async getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox[]> {
|
@@ -224,7 +251,9 @@ export class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
|
224
251
|
}
|
225
252
|
|
226
253
|
reduceTransaction(): Promise<TransactionReductionResult> {
|
227
|
-
throw new NotSupportedError(
|
254
|
+
throw new NotSupportedError(
|
255
|
+
"Transaction reducing is not supported by ergo-graphql."
|
256
|
+
);
|
228
257
|
}
|
229
258
|
}
|
230
259
|
|
@@ -237,15 +266,19 @@ function buildGqlBoxQueryArgs(where: GraphQLBoxWhere) {
|
|
237
266
|
tokenId: where.tokenId,
|
238
267
|
skip: 0,
|
239
268
|
take: PAGE_SIZE
|
240
|
-
} satisfies
|
269
|
+
} satisfies QueryBoxesArgs;
|
241
270
|
|
242
271
|
const addresses = merge(where.addresses, where.address);
|
243
272
|
if (some(addresses)) {
|
244
273
|
const trees = addresses.map((address) =>
|
245
|
-
typeof address === "string"
|
274
|
+
typeof address === "string"
|
275
|
+
? ErgoAddress.fromBase58(address).ergoTree
|
276
|
+
: address.ergoTree
|
246
277
|
);
|
247
278
|
|
248
|
-
args.ergoTrees = uniq(
|
279
|
+
args.ergoTrees = uniq(
|
280
|
+
some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees
|
281
|
+
);
|
249
282
|
}
|
250
283
|
|
251
284
|
return args;
|
@@ -259,12 +292,12 @@ function merge<T>(array?: T[], el?: T) {
|
|
259
292
|
return Array.from(set.values());
|
260
293
|
}
|
261
294
|
|
262
|
-
function hasMempool(data:
|
263
|
-
return !!(data as
|
295
|
+
function hasMempool(data: unknown): data is UnconfirmedBoxesResponse {
|
296
|
+
return !!(data as UnconfirmedBoxesResponse)?.mempool?.boxes;
|
264
297
|
}
|
265
298
|
|
266
|
-
function hasConfirmed(data:
|
267
|
-
return !!(data as
|
299
|
+
function hasConfirmed(data: unknown): data is ConfirmedBoxesResponse {
|
300
|
+
return !!(data as ConfirmedBoxesResponse)?.boxes;
|
268
301
|
}
|
269
302
|
|
270
303
|
function asConfirmed(confirmed: boolean) {
|
@@ -1,12 +1,15 @@
|
|
1
1
|
const B = [
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
"query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)",
|
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"
|
5
5
|
];
|
6
6
|
|
7
7
|
export const CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;
|
8
8
|
export const UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
9
9
|
export const ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
10
|
-
export const HEADERS_QUERY =
|
11
|
-
|
12
|
-
export const
|
10
|
+
export const HEADERS_QUERY =
|
11
|
+
"query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }";
|
12
|
+
export const CHECK_TX_MUTATION =
|
13
|
+
"mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }";
|
14
|
+
export const SEND_TX_MUTATION =
|
15
|
+
"mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }";
|
package/src/index.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export * from "./ergo-graphql";
|
1
|
+
export * from "./ergo-graphql/ergoGraphQLProvider";
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import type {
|
2
2
|
Base58String,
|
3
3
|
BlockHeader,
|
4
4
|
Box,
|
@@ -9,8 +9,8 @@ import {
|
|
9
9
|
TransactionId,
|
10
10
|
UnsignedTransaction
|
11
11
|
} from "@fleet-sdk/common";
|
12
|
-
import { ErgoAddress } from "@fleet-sdk/core";
|
13
|
-
import { RequireAtLeastOne } from "type-fest";
|
12
|
+
import type { ErgoAddress } from "@fleet-sdk/core";
|
13
|
+
import type { RequireAtLeastOne } from "type-fest";
|
14
14
|
|
15
15
|
export type BoxSource = "blockchain" | "mempool" | "blockchain+mempool";
|
16
16
|
|
@@ -63,8 +63,12 @@ export type TransactionReductionSuccess = {
|
|
63
63
|
reducedTransaction: HexString;
|
64
64
|
};
|
65
65
|
|
66
|
-
export type TransactionEvaluationResult =
|
67
|
-
|
66
|
+
export type TransactionEvaluationResult =
|
67
|
+
| TransactionEvaluationError
|
68
|
+
| TransactionEvaluationSuccess;
|
69
|
+
export type TransactionReductionResult =
|
70
|
+
| TransactionEvaluationError
|
71
|
+
| TransactionReductionSuccess;
|
68
72
|
|
69
73
|
/**
|
70
74
|
* Represents a blockchain provider that can interact with the blockchain.
|
@@ -89,15 +93,21 @@ export interface IBlockchainProvider<B extends BoxWhere> {
|
|
89
93
|
/**
|
90
94
|
* Check for transaction validity without broadcasting it to the network.
|
91
95
|
*/
|
92
|
-
checkTransaction(
|
96
|
+
checkTransaction(
|
97
|
+
transaction: SignedTransaction
|
98
|
+
): Promise<TransactionEvaluationResult>;
|
93
99
|
|
94
100
|
/**
|
95
101
|
* Broadcast a transaction to the network.
|
96
102
|
*/
|
97
|
-
submitTransaction(
|
103
|
+
submitTransaction(
|
104
|
+
transaction: SignedTransaction
|
105
|
+
): Promise<TransactionEvaluationResult>;
|
98
106
|
|
99
107
|
/**
|
100
108
|
* Evaluate a transaction and return Base16-encoded evaluation result.
|
101
109
|
*/
|
102
|
-
reduceTransaction(
|
110
|
+
reduceTransaction(
|
111
|
+
transaction: UnsignedTransaction
|
112
|
+
): Promise<TransactionReductionResult>;
|
103
113
|
}
|
package/src/utils/_tests.ts
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
export const mockResponse = (data: string) => {
|
2
|
-
return {
|
2
|
+
return {
|
3
|
+
text: () => new Promise((resolve) => resolve(data))
|
4
|
+
} as unknown as Response;
|
3
5
|
};
|
4
6
|
|
5
7
|
export const mockChunkedResponse = (chunks: string[]) => {
|
6
8
|
let i = 0;
|
7
|
-
return {
|
9
|
+
return {
|
10
|
+
text: () => new Promise((resolve) => resolve(chunks[i++]))
|
11
|
+
} as unknown as Response;
|
8
12
|
};
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { describe, expectTypeOf, it } from "vitest";
|
2
2
|
import {
|
3
3
|
createGqlOperation,
|
4
|
-
GraphQLOperation,
|
5
|
-
GraphQLResponse,
|
6
|
-
GraphQLSuccessResponse,
|
7
|
-
GraphQLVariables
|
4
|
+
type GraphQLOperation,
|
5
|
+
type GraphQLResponse,
|
6
|
+
type GraphQLSuccessResponse,
|
7
|
+
type GraphQLVariables
|
8
8
|
} from "./graphql";
|
9
9
|
|
10
10
|
describe("createGqlOperation() types", () => {
|
@@ -12,12 +12,20 @@ describe("createGqlOperation() types", () => {
|
|
12
12
|
const url = "https://gql.example.com/";
|
13
13
|
|
14
14
|
it("Should infer the correct type when throwOnNonNetworkErrors is set to true", () => {
|
15
|
-
const throwable = createGqlOperation(query, {
|
15
|
+
const throwable = createGqlOperation(query, {
|
16
|
+
throwOnNonNetworkErrors: true,
|
17
|
+
url
|
18
|
+
});
|
16
19
|
expectTypeOf(throwable).toMatchTypeOf<
|
17
20
|
GraphQLOperation<GraphQLSuccessResponse, GraphQLVariables>
|
18
21
|
>();
|
19
22
|
|
20
|
-
const notThrowable = createGqlOperation(query, {
|
21
|
-
|
23
|
+
const notThrowable = createGqlOperation(query, {
|
24
|
+
throwOnNonNetworkErrors: false,
|
25
|
+
url
|
26
|
+
});
|
27
|
+
expectTypeOf(notThrowable).toMatchTypeOf<
|
28
|
+
GraphQLOperation<GraphQLResponse, GraphQLVariables>
|
29
|
+
>();
|
22
30
|
});
|
23
31
|
});
|
package/src/utils/graphql.ts
CHANGED
@@ -32,11 +32,14 @@ export interface GraphQLErrorResponse {
|
|
32
32
|
errors: GraphQLError[];
|
33
33
|
}
|
34
34
|
|
35
|
-
export type GraphQLResponse<T = unknown> =
|
35
|
+
export type GraphQLResponse<T = unknown> =
|
36
|
+
| GraphQLSuccessResponse<T>
|
37
|
+
| GraphQLErrorResponse;
|
36
38
|
|
37
|
-
export type GraphQLOperation<
|
38
|
-
|
39
|
-
|
39
|
+
export type GraphQLOperation<
|
40
|
+
R extends GraphQLResponse,
|
41
|
+
V extends GraphQLVariables
|
42
|
+
> = (variables?: V) => Promise<R>;
|
40
43
|
|
41
44
|
export interface ResponseParser {
|
42
45
|
parse<T>(text: string): T;
|
@@ -62,15 +65,24 @@ export interface GraphQLThrowableOptions extends GraphQLRequestOptions {
|
|
62
65
|
throwOnNonNetworkErrors: true;
|
63
66
|
}
|
64
67
|
|
65
|
-
export function createGqlOperation<
|
68
|
+
export function createGqlOperation<
|
69
|
+
R,
|
70
|
+
V extends GraphQLVariables = GraphQLVariables
|
71
|
+
>(
|
66
72
|
query: string,
|
67
73
|
options: GraphQLThrowableOptions
|
68
74
|
): GraphQLOperation<GraphQLSuccessResponse<R>, V>;
|
69
|
-
export function createGqlOperation<
|
75
|
+
export function createGqlOperation<
|
76
|
+
R,
|
77
|
+
V extends GraphQLVariables = GraphQLVariables
|
78
|
+
>(
|
70
79
|
query: string,
|
71
80
|
options: GraphQLRequestOptions
|
72
81
|
): GraphQLOperation<GraphQLResponse<R>, V>;
|
73
|
-
export function createGqlOperation<
|
82
|
+
export function createGqlOperation<
|
83
|
+
R,
|
84
|
+
V extends GraphQLVariables = GraphQLVariables
|
85
|
+
>(
|
74
86
|
query: string,
|
75
87
|
options: GraphQLRequestOptions
|
76
88
|
): GraphQLOperation<GraphQLResponse<R>, V> {
|
@@ -87,10 +99,18 @@ export function createGqlOperation<R, V extends GraphQLVariables = GraphQLVariab
|
|
87
99
|
});
|
88
100
|
|
89
101
|
const rawData = await response.text();
|
90
|
-
const parsedData = (options.parser ?? JSON).parse(
|
91
|
-
|
92
|
-
|
93
|
-
|
102
|
+
const parsedData = (options.parser ?? JSON).parse(
|
103
|
+
rawData
|
104
|
+
) as GraphQLResponse<R>;
|
105
|
+
|
106
|
+
if (
|
107
|
+
options.throwOnNonNetworkErrors &&
|
108
|
+
some(parsedData.errors) &&
|
109
|
+
isEmpty(parsedData.data)
|
110
|
+
) {
|
111
|
+
throw new BlockchainProviderError(parsedData.errors[0].message, {
|
112
|
+
cause: parsedData.errors
|
113
|
+
});
|
94
114
|
}
|
95
115
|
|
96
116
|
return parsedData;
|
@@ -106,5 +126,7 @@ export function getOpName(query: string): string | undefined {
|
|
106
126
|
}
|
107
127
|
|
108
128
|
export function isRequestParam(obj: unknown): obj is GraphQLRequestOptions {
|
109
|
-
return
|
129
|
+
return (
|
130
|
+
typeof obj === "object" && (obj as GraphQLRequestOptions).url !== undefined
|
131
|
+
);
|
110
132
|
}
|
@@ -1 +0,0 @@
|
|
1
|
-
export * from "./ergoGraphQLProvider";
|
package/src/types/index.ts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export * from "./blockchainProvider";
|