@fleet-sdk/blockchain-providers 0.3.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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.cjs.js +215 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.mts +281 -0
- package/dist/index.d.ts +281 -0
- package/dist/index.esm.js +213 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +47 -0
- package/src/ergo-graphql/ergoGraphQLProvider.ts +280 -0
- package/src/ergo-graphql/index.ts +1 -0
- package/src/ergo-graphql/queries.ts +12 -0
- package/src/index.ts +1 -0
- package/src/types/blockchainProvider.ts +103 -0
- package/src/types/index.ts +1 -0
- package/src/utils/_tests.ts +8 -0
- package/src/utils/graphql.test-d.ts +23 -0
- package/src/utils/graphql.ts +110 -0
- package/src/utils/index.ts +2 -0
package/dist/index.d.ts
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
import { BoxId, HexString, Base58String, TokenId, Box, BlockHeader, SignedTransaction, UnsignedTransaction, TransactionId } from '@fleet-sdk/common';
|
2
|
+
import { ErgoAddress } from '@fleet-sdk/core';
|
3
|
+
|
4
|
+
declare global {
|
5
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- It has to be an `interface` so that it can be merged.
|
6
|
+
interface SymbolConstructor {
|
7
|
+
readonly observable: symbol;
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
Returns a boolean for whether the two given types are equal.
|
13
|
+
|
14
|
+
@link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
|
15
|
+
@link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796
|
16
|
+
|
17
|
+
Use-cases:
|
18
|
+
- If you want to make a conditional branch based on the result of a comparison of two types.
|
19
|
+
|
20
|
+
@example
|
21
|
+
```
|
22
|
+
import type {IsEqual} from 'type-fest';
|
23
|
+
|
24
|
+
// This type returns a boolean for whether the given array includes the given item.
|
25
|
+
// `IsEqual` is used to compare the given array at position 0 and the given item and then return true if they are equal.
|
26
|
+
type Includes<Value extends readonly any[], Item> =
|
27
|
+
Value extends readonly [Value[0], ...infer rest]
|
28
|
+
? IsEqual<Value[0], Item> extends true
|
29
|
+
? true
|
30
|
+
: Includes<rest, Item>
|
31
|
+
: false;
|
32
|
+
```
|
33
|
+
|
34
|
+
@category Type Guard
|
35
|
+
@category Utilities
|
36
|
+
*/
|
37
|
+
type IsEqual<A, B> =
|
38
|
+
(<G>() => G extends A ? 1 : 2) extends
|
39
|
+
(<G>() => G extends B ? 1 : 2)
|
40
|
+
? true
|
41
|
+
: false;
|
42
|
+
|
43
|
+
/**
|
44
|
+
Filter out keys from an object.
|
45
|
+
|
46
|
+
Returns `never` if `Exclude` is strictly equal to `Key`.
|
47
|
+
Returns `never` if `Key` extends `Exclude`.
|
48
|
+
Returns `Key` otherwise.
|
49
|
+
|
50
|
+
@example
|
51
|
+
```
|
52
|
+
type Filtered = Filter<'foo', 'foo'>;
|
53
|
+
//=> never
|
54
|
+
```
|
55
|
+
|
56
|
+
@example
|
57
|
+
```
|
58
|
+
type Filtered = Filter<'bar', string>;
|
59
|
+
//=> never
|
60
|
+
```
|
61
|
+
|
62
|
+
@example
|
63
|
+
```
|
64
|
+
type Filtered = Filter<'bar', 'foo'>;
|
65
|
+
//=> 'bar'
|
66
|
+
```
|
67
|
+
|
68
|
+
@see {Except}
|
69
|
+
*/
|
70
|
+
type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ? never : (KeyType extends ExcludeType ? never : KeyType);
|
71
|
+
|
72
|
+
type ExceptOptions = {
|
73
|
+
/**
|
74
|
+
Disallow assigning non-specified properties.
|
75
|
+
|
76
|
+
Note that any omitted properties in the resulting type will be present in autocomplete as `undefined`.
|
77
|
+
|
78
|
+
@default false
|
79
|
+
*/
|
80
|
+
requireExactProps?: boolean;
|
81
|
+
};
|
82
|
+
|
83
|
+
/**
|
84
|
+
Create a type from an object type without certain keys.
|
85
|
+
|
86
|
+
We recommend setting the `requireExactProps` option to `true`.
|
87
|
+
|
88
|
+
This type is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). The `Omit` type does not restrict the omitted keys to be keys present on the given type, while `Except` does. The benefits of a stricter type are avoiding typos and allowing the compiler to pick up on rename refactors automatically.
|
89
|
+
|
90
|
+
This type was proposed to the TypeScript team, which declined it, saying they prefer that libraries implement stricter versions of the built-in types ([microsoft/TypeScript#30825](https://github.com/microsoft/TypeScript/issues/30825#issuecomment-523668235)).
|
91
|
+
|
92
|
+
@example
|
93
|
+
```
|
94
|
+
import type {Except} from 'type-fest';
|
95
|
+
|
96
|
+
type Foo = {
|
97
|
+
a: number;
|
98
|
+
b: string;
|
99
|
+
};
|
100
|
+
|
101
|
+
type FooWithoutA = Except<Foo, 'a'>;
|
102
|
+
//=> {b: string}
|
103
|
+
|
104
|
+
const fooWithoutA: FooWithoutA = {a: 1, b: '2'};
|
105
|
+
//=> errors: 'a' does not exist in type '{ b: string; }'
|
106
|
+
|
107
|
+
type FooWithoutB = Except<Foo, 'b', {requireExactProps: true}>;
|
108
|
+
//=> {a: number} & Partial<Record<"b", never>>
|
109
|
+
|
110
|
+
const fooWithoutB: FooWithoutB = {a: 1, b: '2'};
|
111
|
+
//=> errors at 'b': Type 'string' is not assignable to type 'undefined'.
|
112
|
+
```
|
113
|
+
|
114
|
+
@category Object
|
115
|
+
*/
|
116
|
+
type Except<ObjectType, KeysType extends keyof ObjectType, Options extends ExceptOptions = {requireExactProps: false}> = {
|
117
|
+
[KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType];
|
118
|
+
} & (Options['requireExactProps'] extends true
|
119
|
+
? Partial<Record<KeysType, never>>
|
120
|
+
: {});
|
121
|
+
|
122
|
+
/**
|
123
|
+
Create a type that requires at least one of the given keys. The remaining keys are kept as is.
|
124
|
+
|
125
|
+
@example
|
126
|
+
```
|
127
|
+
import type {RequireAtLeastOne} from 'type-fest';
|
128
|
+
|
129
|
+
type Responder = {
|
130
|
+
text?: () => string;
|
131
|
+
json?: () => string;
|
132
|
+
secure?: boolean;
|
133
|
+
};
|
134
|
+
|
135
|
+
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
|
136
|
+
json: () => '{"message": "ok"}',
|
137
|
+
secure: true
|
138
|
+
};
|
139
|
+
```
|
140
|
+
|
141
|
+
@category Object
|
142
|
+
*/
|
143
|
+
type RequireAtLeastOne<
|
144
|
+
ObjectType,
|
145
|
+
KeysType extends keyof ObjectType = keyof ObjectType,
|
146
|
+
> = {
|
147
|
+
// For each `Key` in `KeysType` make a mapped type:
|
148
|
+
[Key in KeysType]-?: Required<Pick<ObjectType, Key>> & // 1. Make `Key`'s type required
|
149
|
+
// 2. Make all other keys in `KeysType` optional
|
150
|
+
Partial<Pick<ObjectType, Exclude<KeysType, Key>>>;
|
151
|
+
}[KeysType] &
|
152
|
+
// 3. Add the remaining keys not in `KeysType`
|
153
|
+
Except<ObjectType, KeysType>;
|
154
|
+
|
155
|
+
type BoxSource = "blockchain" | "mempool" | "blockchain+mempool";
|
156
|
+
type BoxQuery<W extends BoxWhere> = {
|
157
|
+
/** The query to filter boxes. */
|
158
|
+
where: RequireAtLeastOne<W>;
|
159
|
+
/**
|
160
|
+
* The source of boxes to query.
|
161
|
+
* @default "blockchain+mempool"
|
162
|
+
*/
|
163
|
+
from?: BoxSource;
|
164
|
+
};
|
165
|
+
type HeaderQuery = {
|
166
|
+
take: number;
|
167
|
+
};
|
168
|
+
type BoxWhere = {
|
169
|
+
/** Base16-encoded BoxId */
|
170
|
+
boxId?: BoxId;
|
171
|
+
/** Base16-encoded ErgoTree */
|
172
|
+
ergoTree?: HexString;
|
173
|
+
/** Base58-encoded address */
|
174
|
+
address?: ErgoAddress | Base58String;
|
175
|
+
/** Base16-encoded contract template hash */
|
176
|
+
templateHash?: HexString;
|
177
|
+
/** Base16-encoded TokenId */
|
178
|
+
tokenId?: TokenId;
|
179
|
+
};
|
180
|
+
type ChainProviderBox = Box<bigint> & {
|
181
|
+
confirmed: boolean;
|
182
|
+
};
|
183
|
+
type TransactionEvaluationError = {
|
184
|
+
success: false;
|
185
|
+
message: string;
|
186
|
+
};
|
187
|
+
type TransactionEvaluationSuccess = {
|
188
|
+
success: true;
|
189
|
+
transactionId: TransactionId;
|
190
|
+
};
|
191
|
+
type TransactionReductionSuccess = {
|
192
|
+
success: true;
|
193
|
+
reducedTransaction: HexString;
|
194
|
+
};
|
195
|
+
type TransactionEvaluationResult = TransactionEvaluationError | TransactionEvaluationSuccess;
|
196
|
+
type TransactionReductionResult = TransactionEvaluationError | TransactionReductionSuccess;
|
197
|
+
/**
|
198
|
+
* Represents a blockchain provider that can interact with the blockchain.
|
199
|
+
* @template B The type of the box query used by the provider.
|
200
|
+
*/
|
201
|
+
interface IBlockchainProvider<B extends BoxWhere> {
|
202
|
+
/**
|
203
|
+
* Get boxes.
|
204
|
+
*/
|
205
|
+
getBoxes(query: BoxQuery<B>): Promise<ChainProviderBox[]>;
|
206
|
+
/**
|
207
|
+
* Stream boxes.
|
208
|
+
*/
|
209
|
+
streamBoxes(query: BoxQuery<B>): AsyncIterable<ChainProviderBox[]>;
|
210
|
+
/**
|
211
|
+
* Get headers.
|
212
|
+
*/
|
213
|
+
getHeaders(query: HeaderQuery): Promise<BlockHeader[]>;
|
214
|
+
/**
|
215
|
+
* Check for transaction validity without broadcasting it to the network.
|
216
|
+
*/
|
217
|
+
checkTransaction(transaction: SignedTransaction): Promise<TransactionEvaluationResult>;
|
218
|
+
/**
|
219
|
+
* Broadcast a transaction to the network.
|
220
|
+
*/
|
221
|
+
submitTransaction(transaction: SignedTransaction): Promise<TransactionEvaluationResult>;
|
222
|
+
/**
|
223
|
+
* Evaluate a transaction and return Base16-encoded evaluation result.
|
224
|
+
*/
|
225
|
+
reduceTransaction(transaction: UnsignedTransaction): Promise<TransactionReductionResult>;
|
226
|
+
}
|
227
|
+
|
228
|
+
type Credentials = RequestCredentials;
|
229
|
+
type Headers = HeadersInit;
|
230
|
+
type Fetcher = typeof fetch;
|
231
|
+
type GraphQLVariables = Record<string, unknown> | null;
|
232
|
+
interface GraphQLError {
|
233
|
+
message: string;
|
234
|
+
}
|
235
|
+
interface GraphQLSuccessResponse<T = unknown> {
|
236
|
+
data: T;
|
237
|
+
errors: null;
|
238
|
+
}
|
239
|
+
interface GraphQLErrorResponse {
|
240
|
+
data: null;
|
241
|
+
errors: GraphQLError[];
|
242
|
+
}
|
243
|
+
type GraphQLResponse<T = unknown> = GraphQLSuccessResponse<T> | GraphQLErrorResponse;
|
244
|
+
type GraphQLOperation<R extends GraphQLResponse, V extends GraphQLVariables> = (variables?: V) => Promise<R>;
|
245
|
+
interface ResponseParser {
|
246
|
+
parse<T>(text: string): T;
|
247
|
+
stringify<T>(value: T): string;
|
248
|
+
}
|
249
|
+
interface GraphQLRequestOptions {
|
250
|
+
url: URL | string;
|
251
|
+
headers?: Headers;
|
252
|
+
parser?: ResponseParser;
|
253
|
+
fetcher?: Fetcher;
|
254
|
+
credentials?: Credentials;
|
255
|
+
throwOnNonNetworkErrors?: boolean;
|
256
|
+
}
|
257
|
+
|
258
|
+
type GraphQLBoxWhere = BoxWhere & {
|
259
|
+
/** Base16-encoded BoxIds */
|
260
|
+
boxIds?: HexString[];
|
261
|
+
/** Base16-encoded ErgoTrees */
|
262
|
+
ergoTrees?: HexString[];
|
263
|
+
/** Base58-encoded addresses or `ErgoAddress` objects */
|
264
|
+
addresses?: (Base58String | ErgoAddress)[];
|
265
|
+
};
|
266
|
+
type GraphQLBoxQuery = BoxQuery<GraphQLBoxWhere>;
|
267
|
+
type ErgoGraphQLRequestOptions = Omit<GraphQLRequestOptions, "throwOnNonNetworkError">;
|
268
|
+
declare class ErgoGraphQLProvider implements IBlockchainProvider<BoxWhere> {
|
269
|
+
#private;
|
270
|
+
constructor(url: string | URL);
|
271
|
+
constructor(url: ErgoGraphQLRequestOptions);
|
272
|
+
streamBoxes(query: GraphQLBoxQuery): AsyncGenerator<ChainProviderBox[]>;
|
273
|
+
getBoxes(query: GraphQLBoxQuery): Promise<ChainProviderBox[]>;
|
274
|
+
getHeaders(query: HeaderQuery): Promise<BlockHeader[]>;
|
275
|
+
createOperation<R, V extends GraphQLVariables = GraphQLVariables>(query: string, options?: Partial<ErgoGraphQLRequestOptions>): GraphQLOperation<GraphQLSuccessResponse<R>, V>;
|
276
|
+
checkTransaction(signedTransaction: SignedTransaction): Promise<TransactionEvaluationResult>;
|
277
|
+
submitTransaction(signedTransaction: SignedTransaction): Promise<TransactionEvaluationResult>;
|
278
|
+
reduceTransaction(): Promise<TransactionReductionResult>;
|
279
|
+
}
|
280
|
+
|
281
|
+
export { ErgoGraphQLProvider, ErgoGraphQLRequestOptions, GraphQLBoxQuery, GraphQLBoxWhere };
|
@@ -0,0 +1,213 @@
|
|
1
|
+
import { isEmpty, some, uniqBy, orderBy, ensureDefaults, NotSupportedError, uniq, isUndefined, clearUndefined, BlockchainProviderError } from '@fleet-sdk/common';
|
2
|
+
import { ErgoAddress } from '@fleet-sdk/core';
|
3
|
+
|
4
|
+
// src/ergo-graphql/ergoGraphQLProvider.ts
|
5
|
+
var OP_NAME_REGEX = /(query|mutation)\s?([\w\-_]+)?/;
|
6
|
+
var DEFAULT_HEADERS = {
|
7
|
+
"content-type": "application/json; charset=utf-8",
|
8
|
+
accept: "application/graphql-response+json, application/json"
|
9
|
+
};
|
10
|
+
function createGqlOperation(query, options) {
|
11
|
+
return async (variables) => {
|
12
|
+
const response = await (options.fetcher ?? fetch)(options.url, {
|
13
|
+
method: "POST",
|
14
|
+
headers: ensureDefaults(options.headers, DEFAULT_HEADERS),
|
15
|
+
credentials: options.credentials,
|
16
|
+
body: (options.parser ?? JSON).stringify({
|
17
|
+
operationName: getOpName(query),
|
18
|
+
query,
|
19
|
+
variables: variables ? clearUndefined(variables) : void 0
|
20
|
+
})
|
21
|
+
});
|
22
|
+
const rawData = await response.text();
|
23
|
+
const parsedData = (options.parser ?? JSON).parse(rawData);
|
24
|
+
if (options.throwOnNonNetworkErrors && some(parsedData.errors) && isEmpty(parsedData.data)) {
|
25
|
+
throw new BlockchainProviderError(parsedData.errors[0].message, { cause: parsedData.errors });
|
26
|
+
}
|
27
|
+
return parsedData;
|
28
|
+
};
|
29
|
+
}
|
30
|
+
function getOpName(query) {
|
31
|
+
return OP_NAME_REGEX.exec(query)?.at(2);
|
32
|
+
}
|
33
|
+
function isRequestParam(obj) {
|
34
|
+
return typeof obj === "object" && obj.url !== void 0;
|
35
|
+
}
|
36
|
+
|
37
|
+
// src/ergo-graphql/queries.ts
|
38
|
+
var B = [
|
39
|
+
`query boxes($spent: Boolean! $boxIds: [String!] $ergoTrees: [String!] $ergoTreeTemplateHash: String $tokenId: String $skip: Int $take: Int)`,
|
40
|
+
`boxIds: $boxIds ergoTrees: $ergoTrees ergoTreeTemplateHash: $ergoTreeTemplateHash tokenId: $tokenId skip: $skip take: $take`,
|
41
|
+
`boxId transactionId index value creationHeight ergoTree assets { tokenId amount } additionalRegisters beingSpent`
|
42
|
+
];
|
43
|
+
var CONF_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } }`;
|
44
|
+
var UNCONF_BOXES_QUERY = `${B[0]} { mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
45
|
+
var ALL_BOXES_QUERY = `${B[0]} { boxes(spent: $spent ${B[1]}) { ${B[2]} } mempool { boxes(${B[1]}) { ${B[2]} } } }`;
|
46
|
+
var HEADERS_QUERY = `query blockHeaders($take: Int) { blockHeaders(take: $take) {headerId timestamp version adProofsRoot stateRoot transactionsRoot nBits extensionHash powSolutions height difficulty parentId votes } }`;
|
47
|
+
var CHECK_TX_MUTATION = `mutation checkTransaction($signedTransaction: SignedTransaction!) { checkTransaction(signedTransaction: $signedTransaction) }`;
|
48
|
+
var SEND_TX_MUTATION = `mutation submitTransaction($signedTransaction: SignedTransaction!) { submitTransaction(signedTransaction: $signedTransaction) }`;
|
49
|
+
|
50
|
+
// src/ergo-graphql/ergoGraphQLProvider.ts
|
51
|
+
var PAGE_SIZE = 50;
|
52
|
+
var ErgoGraphQLProvider = class {
|
53
|
+
#options;
|
54
|
+
#getConfBoxes;
|
55
|
+
#getUnconfBoxes;
|
56
|
+
#getAllBoxes;
|
57
|
+
#getHeaders;
|
58
|
+
#checkTx;
|
59
|
+
#sendTx;
|
60
|
+
constructor(optOrUrl) {
|
61
|
+
this.#options = {
|
62
|
+
...isRequestParam(optOrUrl) ? optOrUrl : { url: optOrUrl },
|
63
|
+
throwOnNonNetworkErrors: true
|
64
|
+
};
|
65
|
+
this.#getConfBoxes = this.createOperation(CONF_BOXES_QUERY);
|
66
|
+
this.#getUnconfBoxes = this.createOperation(UNCONF_BOXES_QUERY);
|
67
|
+
this.#getAllBoxes = this.createOperation(ALL_BOXES_QUERY);
|
68
|
+
this.#getHeaders = this.createOperation(HEADERS_QUERY);
|
69
|
+
this.#checkTx = this.createOperation(CHECK_TX_MUTATION);
|
70
|
+
this.#sendTx = this.createOperation(SEND_TX_MUTATION);
|
71
|
+
}
|
72
|
+
#fetchBoxes(args, inclConf, inclUnconf) {
|
73
|
+
if (inclConf && inclUnconf) {
|
74
|
+
return this.#getAllBoxes(args);
|
75
|
+
} else if (inclUnconf) {
|
76
|
+
return this.#getUnconfBoxes(args);
|
77
|
+
} else {
|
78
|
+
return this.#getConfBoxes(args);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
async *streamBoxes(query) {
|
82
|
+
if (isEmpty(query.where)) {
|
83
|
+
throw new Error("Cannot fetch unspent boxes without a where clause.");
|
84
|
+
}
|
85
|
+
const notBeingSpent = (box) => !box.beingSpent;
|
86
|
+
const returnedBoxIds = /* @__PURE__ */ new Set();
|
87
|
+
const { where, from } = query;
|
88
|
+
const args = buildGqlBoxQueryArgs(where);
|
89
|
+
let fetchFromChain = from !== "mempool";
|
90
|
+
let fetchFromMempool = from !== "blockchain";
|
91
|
+
const isMempoolAware = fetchFromMempool;
|
92
|
+
do {
|
93
|
+
const response = await this.#fetchBoxes(args, fetchFromChain, fetchFromMempool);
|
94
|
+
const { data } = response;
|
95
|
+
let boxes = [];
|
96
|
+
if (fetchFromChain && hasConfirmed(data)) {
|
97
|
+
if (some(data.boxes)) {
|
98
|
+
const confirmedBoxes = (isMempoolAware ? data.boxes.filter(notBeingSpent) : data.boxes).map(asConfirmed(true));
|
99
|
+
boxes = boxes.concat(confirmedBoxes);
|
100
|
+
}
|
101
|
+
fetchFromChain = data.boxes.length === PAGE_SIZE;
|
102
|
+
}
|
103
|
+
if (isMempoolAware && hasMempool(data)) {
|
104
|
+
if (some(data.mempool.boxes)) {
|
105
|
+
const mempoolBoxes = data.mempool.boxes.filter(notBeingSpent).map(asConfirmed(false));
|
106
|
+
boxes = boxes.concat(mempoolBoxes);
|
107
|
+
}
|
108
|
+
fetchFromMempool = data.mempool.boxes.length === PAGE_SIZE;
|
109
|
+
}
|
110
|
+
if (some(boxes)) {
|
111
|
+
if (boxes.some((box) => returnedBoxIds.has(box.boxId))) {
|
112
|
+
boxes = boxes.filter((b) => !returnedBoxIds.has(b.boxId));
|
113
|
+
}
|
114
|
+
if (some(boxes)) {
|
115
|
+
boxes = uniqBy(boxes, (box) => box.boxId);
|
116
|
+
boxes.forEach((box) => returnedBoxIds.add(box.boxId));
|
117
|
+
yield boxes;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
if (fetchFromChain || fetchFromMempool)
|
121
|
+
args.skip += PAGE_SIZE;
|
122
|
+
} while (fetchFromChain || fetchFromMempool);
|
123
|
+
}
|
124
|
+
async getBoxes(query) {
|
125
|
+
let boxes = [];
|
126
|
+
for await (const chunk of this.streamBoxes(query)) {
|
127
|
+
boxes = boxes.concat(chunk);
|
128
|
+
}
|
129
|
+
return orderBy(boxes, (box) => box.creationHeight);
|
130
|
+
}
|
131
|
+
async getHeaders(query) {
|
132
|
+
const response = await this.#getHeaders(query);
|
133
|
+
return response.data?.blockHeaders.map((header) => ({
|
134
|
+
...header,
|
135
|
+
id: header.headerId,
|
136
|
+
timestamp: Number(header.timestamp),
|
137
|
+
nBits: Number(header.nBits),
|
138
|
+
votes: header.votes.join("")
|
139
|
+
})) ?? [];
|
140
|
+
}
|
141
|
+
createOperation(query, options) {
|
142
|
+
const opt = ensureDefaults(options, this.#options);
|
143
|
+
opt.throwOnNonNetworkErrors = true;
|
144
|
+
return createGqlOperation(query, opt);
|
145
|
+
}
|
146
|
+
async checkTransaction(signedTransaction) {
|
147
|
+
try {
|
148
|
+
const response = await this.#checkTx({ signedTransaction });
|
149
|
+
return { success: true, transactionId: response.data.checkTransaction };
|
150
|
+
} catch (e) {
|
151
|
+
return { success: false, message: e.message };
|
152
|
+
}
|
153
|
+
}
|
154
|
+
async submitTransaction(signedTransaction) {
|
155
|
+
try {
|
156
|
+
const response = await this.#sendTx({ signedTransaction });
|
157
|
+
return { success: true, transactionId: response.data.submitTransaction };
|
158
|
+
} catch (e) {
|
159
|
+
return { success: false, message: e.message };
|
160
|
+
}
|
161
|
+
}
|
162
|
+
reduceTransaction() {
|
163
|
+
throw new NotSupportedError("Transaction reducing is not supported by ergo-graphql.");
|
164
|
+
}
|
165
|
+
};
|
166
|
+
function buildGqlBoxQueryArgs(where) {
|
167
|
+
const args = {
|
168
|
+
spent: false,
|
169
|
+
boxIds: merge(where.boxIds, where.boxId),
|
170
|
+
ergoTrees: merge(where.ergoTrees, where.ergoTree),
|
171
|
+
ergoTreeTemplateHash: where.templateHash,
|
172
|
+
tokenId: where.tokenId,
|
173
|
+
skip: 0,
|
174
|
+
take: PAGE_SIZE
|
175
|
+
};
|
176
|
+
const addresses = merge(where.addresses, where.address);
|
177
|
+
if (some(addresses)) {
|
178
|
+
const trees = addresses.map(
|
179
|
+
(address) => typeof address === "string" ? ErgoAddress.fromBase58(address).ergoTree : address.ergoTree
|
180
|
+
);
|
181
|
+
args.ergoTrees = uniq(some(args.ergoTrees) ? args.ergoTrees.concat(trees) : trees);
|
182
|
+
}
|
183
|
+
return args;
|
184
|
+
}
|
185
|
+
function merge(array, el) {
|
186
|
+
if (isEmpty(array) && isUndefined(el))
|
187
|
+
return;
|
188
|
+
const set = new Set(array ?? []);
|
189
|
+
if (!isUndefined(el))
|
190
|
+
set.add(el);
|
191
|
+
return Array.from(set.values());
|
192
|
+
}
|
193
|
+
function hasMempool(data) {
|
194
|
+
return !!data?.mempool?.boxes;
|
195
|
+
}
|
196
|
+
function hasConfirmed(data) {
|
197
|
+
return !!data?.boxes;
|
198
|
+
}
|
199
|
+
function asConfirmed(confirmed) {
|
200
|
+
return (box) => ({
|
201
|
+
...box,
|
202
|
+
value: BigInt(box.value),
|
203
|
+
assets: box.assets.map((asset) => ({
|
204
|
+
tokenId: asset.tokenId,
|
205
|
+
amount: BigInt(asset.amount)
|
206
|
+
})),
|
207
|
+
confirmed
|
208
|
+
});
|
209
|
+
}
|
210
|
+
|
211
|
+
export { ErgoGraphQLProvider };
|
212
|
+
//# sourceMappingURL=out.js.map
|
213
|
+
//# sourceMappingURL=index.esm.js.map
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
{
|
2
|
+
"name": "@fleet-sdk/blockchain-providers",
|
3
|
+
"version": "0.3.0",
|
4
|
+
"description": "Blockchain data providers",
|
5
|
+
"main": "./dist/index.cjs.js",
|
6
|
+
"module": "./dist/index.esm.js",
|
7
|
+
"types": "./dist/index.d.ts",
|
8
|
+
"exports": {
|
9
|
+
"require": "./dist/index.cjs.js",
|
10
|
+
"import": "./dist/index.esm.js"
|
11
|
+
},
|
12
|
+
"sideEffects": true,
|
13
|
+
"repository": "fleet-sdk/fleet",
|
14
|
+
"license": "MIT",
|
15
|
+
"publishConfig": {
|
16
|
+
"access": "public",
|
17
|
+
"provenance": true
|
18
|
+
},
|
19
|
+
"keywords": [
|
20
|
+
"ergo",
|
21
|
+
"blockchain",
|
22
|
+
"crypto"
|
23
|
+
],
|
24
|
+
"engines": {
|
25
|
+
"node": ">=14"
|
26
|
+
},
|
27
|
+
"dependencies": {
|
28
|
+
"@fleet-sdk/common": "^0.3.0",
|
29
|
+
"@fleet-sdk/core": "^0.3.0"
|
30
|
+
},
|
31
|
+
"files": [
|
32
|
+
"src",
|
33
|
+
"dist",
|
34
|
+
"!**/*.spec.*",
|
35
|
+
"!**/*.json",
|
36
|
+
"!tests",
|
37
|
+
"CHANGELOG.md",
|
38
|
+
"LICENSE",
|
39
|
+
"README.md"
|
40
|
+
],
|
41
|
+
"devDependencies": {
|
42
|
+
"@ergo-graphql/types": "^0.5.1"
|
43
|
+
},
|
44
|
+
"scripts": {
|
45
|
+
"build": "tsup --config ../../tsup.config.ts"
|
46
|
+
}
|
47
|
+
}
|