@dedot/api 0.18.8 → 1.0.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/chaintypes/substrate/consts.d.ts +2 -2
- package/chaintypes/substrate/consts.js +1 -0
- package/chaintypes/substrate/errors.d.ts +909 -909
- package/chaintypes/substrate/errors.js +1 -0
- package/chaintypes/substrate/events.d.ts +664 -664
- package/chaintypes/substrate/events.js +1 -0
- package/chaintypes/substrate/index.d.ts +13 -16
- package/chaintypes/substrate/index.js +1 -0
- package/chaintypes/substrate/json-rpc.d.ts +2 -2
- package/chaintypes/substrate/json-rpc.js +1 -0
- package/chaintypes/substrate/query.d.ts +509 -527
- package/chaintypes/substrate/query.js +1 -0
- package/chaintypes/substrate/runtime.d.ts +123 -123
- package/chaintypes/substrate/runtime.js +1 -0
- package/chaintypes/substrate/tx.d.ts +1311 -1311
- package/chaintypes/substrate/tx.js +1 -0
- package/chaintypes/substrate/types.js +1 -0
- package/chaintypes/substrate/view-functions.d.ts +2 -2
- package/chaintypes/substrate/view-functions.js +1 -0
- package/cjs/chaintypes/substrate/consts.js +1 -0
- package/cjs/chaintypes/substrate/errors.js +1 -0
- package/cjs/chaintypes/substrate/events.js +1 -0
- package/cjs/chaintypes/substrate/index.js +1 -0
- package/cjs/chaintypes/substrate/json-rpc.js +1 -0
- package/cjs/chaintypes/substrate/query.js +1 -0
- package/cjs/chaintypes/substrate/runtime.js +1 -0
- package/cjs/chaintypes/substrate/tx.js +1 -0
- package/cjs/chaintypes/substrate/types.js +1 -0
- package/cjs/chaintypes/substrate/view-functions.js +1 -0
- package/cjs/client/BaseSubstrateClient.js +45 -2
- package/cjs/client/DedotClient.js +356 -224
- package/cjs/client/LegacyClient.js +99 -23
- package/cjs/client/V2Client.js +316 -0
- package/cjs/client/explorer/LegacyBlockExplorer.js +263 -0
- package/cjs/client/explorer/V2BlockExplorer.js +74 -0
- package/cjs/client/explorer/index.js +18 -0
- package/cjs/client/utils.js +57 -0
- package/cjs/executor/EventExecutor.js +1 -0
- package/cjs/executor/RuntimeApiExecutor.js +49 -2
- package/cjs/executor/StorageQueryExecutor.js +114 -2
- package/cjs/executor/ViewFunctionExecutor.js +21 -2
- package/cjs/executor/v2/StorageQueryExecutorV2.js +7 -1
- package/cjs/executor/validation-helpers.js +181 -0
- package/cjs/extrinsic/submittable/BaseSubmittableExtrinsic.js +86 -2
- package/cjs/extrinsic/submittable/SubmittableExtrinsic.js +38 -16
- package/cjs/extrinsic/submittable/SubmittableExtrinsicV2.js +12 -2
- package/cjs/json-rpc/group/ChainHead/ChainHead.js +226 -21
- package/cjs/storage/QueryableStorage.js +1 -0
- package/client/BaseSubstrateClient.d.ts +34 -18
- package/client/BaseSubstrateClient.js +47 -4
- package/client/DedotClient.d.ts +346 -49
- package/client/DedotClient.js +356 -224
- package/client/LegacyClient.d.ts +22 -17
- package/client/LegacyClient.js +101 -25
- package/client/V2Client.d.ts +75 -0
- package/client/V2Client.js +312 -0
- package/client/explorer/LegacyBlockExplorer.d.ts +68 -0
- package/client/explorer/LegacyBlockExplorer.js +259 -0
- package/client/explorer/V2BlockExplorer.d.ts +40 -0
- package/client/explorer/V2BlockExplorer.js +70 -0
- package/client/explorer/index.d.ts +2 -0
- package/client/explorer/index.js +2 -0
- package/client/utils.d.ts +18 -0
- package/client/utils.js +52 -0
- package/executor/ConstantExecutor.d.ts +1 -2
- package/executor/ErrorExecutor.d.ts +2 -2
- package/executor/EventExecutor.d.ts +2 -2
- package/executor/EventExecutor.js +1 -0
- package/executor/Executor.d.ts +4 -5
- package/executor/RuntimeApiExecutor.d.ts +2 -2
- package/executor/RuntimeApiExecutor.js +27 -3
- package/executor/StorageQueryExecutor.d.ts +3 -2
- package/executor/StorageQueryExecutor.js +92 -3
- package/executor/TxExecutor.d.ts +2 -2
- package/executor/ViewFunctionExecutor.d.ts +2 -2
- package/executor/ViewFunctionExecutor.js +22 -3
- package/executor/v2/RuntimeApiExecutorV2.d.ts +2 -3
- package/executor/v2/StorageQueryExecutorV2.d.ts +3 -3
- package/executor/v2/StorageQueryExecutorV2.js +8 -2
- package/executor/v2/TxExecutorV2.d.ts +5 -5
- package/executor/v2/ViewFunctionExecutorV2.d.ts +2 -3
- package/executor/validation-helpers.d.ts +36 -0
- package/executor/validation-helpers.js +174 -0
- package/extrinsic/extensions/SignedExtension.d.ts +1 -1
- package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +7 -3
- package/extrinsic/submittable/BaseSubmittableExtrinsic.js +64 -3
- package/extrinsic/submittable/SubmittableExtrinsic.d.ts +4 -1
- package/extrinsic/submittable/SubmittableExtrinsic.js +39 -17
- package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +6 -3
- package/extrinsic/submittable/SubmittableExtrinsicV2.js +13 -3
- package/json-rpc/JsonRpcClient.d.ts +6 -6
- package/json-rpc/group/Archive.d.ts +1 -1
- package/json-rpc/group/ChainHead/ChainHead.d.ts +18 -2
- package/json-rpc/group/ChainHead/ChainHead.js +226 -21
- package/json-rpc/group/ChainSpec.d.ts +3 -3
- package/json-rpc/group/Transaction.d.ts +1 -1
- package/json-rpc/group/TransactionWatch.d.ts +1 -1
- package/package.json +9 -9
- package/proxychain.d.ts +3 -4
- package/storage/NewStorageQuery.d.ts +3 -3
- package/storage/QueryableStorage.js +1 -0
- package/types.d.ts +94 -6
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LegacyBlockExplorer = void 0;
|
|
4
|
+
const codecs_1 = require("@dedot/codecs");
|
|
5
|
+
const utils_1 = require("@dedot/utils");
|
|
6
|
+
/**
|
|
7
|
+
* @name LegacyBlockExplorer
|
|
8
|
+
* @description Block explorer implementation for LegacyClient using legacy JSON-RPC methods
|
|
9
|
+
* Optimized to share RPC subscriptions across multiple subscribers using Signal pattern
|
|
10
|
+
*/
|
|
11
|
+
class LegacyBlockExplorer {
|
|
12
|
+
#client;
|
|
13
|
+
// Signals for block subscriptions
|
|
14
|
+
#bestBlockSignal;
|
|
15
|
+
#finalizedBlockSignal;
|
|
16
|
+
// Track RPC subscription cleanup functions
|
|
17
|
+
#bestBlockUnsub;
|
|
18
|
+
#finalizedBlockUnsub;
|
|
19
|
+
constructor(client) {
|
|
20
|
+
this.#client = client;
|
|
21
|
+
this.#bestBlockSignal = new utils_1.Signal();
|
|
22
|
+
this.#finalizedBlockSignal = new utils_1.Signal();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Convert PinnedBlock to BlockInfo format
|
|
26
|
+
*/
|
|
27
|
+
toBlockInfo(header) {
|
|
28
|
+
return {
|
|
29
|
+
hash: header.hash || this.calculateBlockHash(header),
|
|
30
|
+
number: header.number,
|
|
31
|
+
parent: header.parentHash,
|
|
32
|
+
runtimeUpgraded: this.hasNewRuntime(header),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Fill missing blocks when a gap is detected
|
|
37
|
+
* Fetches and emits all missing blocks between lastNumber and currentNumber
|
|
38
|
+
*/
|
|
39
|
+
async fillMissingBlocks(lastNumber, currentNumber, subject, blockType) {
|
|
40
|
+
// No gap if no previous block or current is next sequential block
|
|
41
|
+
if (lastNumber === undefined || currentNumber <= lastNumber + 1) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Gap detected - backfill missing blocks
|
|
45
|
+
const gapSize = currentNumber - lastNumber - 1;
|
|
46
|
+
// Create list of missing block numbers
|
|
47
|
+
const missingNums = Array.from({ length: gapSize }, (_, i) => lastNumber + 1 + i);
|
|
48
|
+
try {
|
|
49
|
+
// Fetch all hashes in parallel
|
|
50
|
+
const hashes = await Promise.all(missingNums.map((num) => this.#client.rpc.chain_getBlockHash(num)));
|
|
51
|
+
// Fetch all headers in parallel
|
|
52
|
+
const headers = await Promise.all(hashes.map((hash) => this.#client.rpc.chain_getHeader(hash)));
|
|
53
|
+
// Emit blocks in order
|
|
54
|
+
for (let i = 0; i < missingNums.length; i++) {
|
|
55
|
+
const hash = hashes[i];
|
|
56
|
+
const header = headers[i];
|
|
57
|
+
if (hash && header) {
|
|
58
|
+
subject.next(this.toBlockInfo({ ...header, hash }));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.error(`Failed to backfill missing ${blockType} blocks:`, error);
|
|
64
|
+
// Continue despite backfill failure
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Start the shared best block subscription if not already active
|
|
69
|
+
*/
|
|
70
|
+
ensureBestBlockSubscription() {
|
|
71
|
+
if (this.#bestBlockUnsub) {
|
|
72
|
+
// Subscription already active
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Use closure pattern to handle async subscription without race conditions
|
|
76
|
+
let done = false;
|
|
77
|
+
let unsub;
|
|
78
|
+
const blockQueue = new utils_1.AsyncQueue();
|
|
79
|
+
// Start RPC subscription (non-blocking)
|
|
80
|
+
this.#client.rpc
|
|
81
|
+
.chain_subscribeNewHeads((header) => {
|
|
82
|
+
if (done) {
|
|
83
|
+
// Unsubscribe was called before subscription completed
|
|
84
|
+
unsub && unsub();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Enqueue block processing to ensure sequential handling
|
|
88
|
+
blockQueue
|
|
89
|
+
.enqueue(async () => {
|
|
90
|
+
const currentHash = this.calculateBlockHash(header);
|
|
91
|
+
// Detect gaps in best block stream (due to reconnection)
|
|
92
|
+
const { number: lastNumber, hash: lastHash } = this.#bestBlockSignal.value || {};
|
|
93
|
+
if (lastHash === currentHash) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Fill missing blocks if gap detected
|
|
97
|
+
await this.fillMissingBlocks(lastNumber, header.number, this.#bestBlockSignal, 'best');
|
|
98
|
+
// Emit current block
|
|
99
|
+
const blockInfo = this.toBlockInfo({ ...header, hash: currentHash });
|
|
100
|
+
this.#bestBlockSignal.next(blockInfo);
|
|
101
|
+
})
|
|
102
|
+
.catch((error) => {
|
|
103
|
+
console.error('Error processing best block:', error);
|
|
104
|
+
});
|
|
105
|
+
})
|
|
106
|
+
.then((rpcUnsub) => {
|
|
107
|
+
unsub = rpcUnsub;
|
|
108
|
+
});
|
|
109
|
+
// Immediately set unsub wrapper to prevent race conditions
|
|
110
|
+
this.#bestBlockUnsub = () => {
|
|
111
|
+
done = true;
|
|
112
|
+
blockQueue.cancel();
|
|
113
|
+
unsub && unsub().catch(utils_1.noop);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
hasNewRuntime(header) {
|
|
117
|
+
return header.digest.logs.some((log) => log.type === 'RuntimeEnvironmentUpdated');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Start the shared finalized block subscription if not already active
|
|
121
|
+
*/
|
|
122
|
+
ensureFinalizedBlockSubscription() {
|
|
123
|
+
if (this.#finalizedBlockUnsub) {
|
|
124
|
+
// Subscription already active
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Use closure pattern to handle async subscription without race conditions
|
|
128
|
+
let done = false;
|
|
129
|
+
let unsub;
|
|
130
|
+
const blockQueue = new utils_1.AsyncQueue();
|
|
131
|
+
// Start RPC subscription (non-blocking)
|
|
132
|
+
this.#client.rpc
|
|
133
|
+
.chain_subscribeFinalizedHeads((header) => {
|
|
134
|
+
if (done) {
|
|
135
|
+
// Unsubscribe was called before subscription completed
|
|
136
|
+
unsub && unsub();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Enqueue block processing to ensure sequential handling
|
|
140
|
+
blockQueue
|
|
141
|
+
.enqueue(async () => {
|
|
142
|
+
// Detect gaps in finalized block stream (due to reconnection)
|
|
143
|
+
const lastNumber = this.#finalizedBlockSignal.value?.number;
|
|
144
|
+
if (lastNumber === header.number) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// Fill missing blocks if gap detected
|
|
148
|
+
await this.fillMissingBlocks(lastNumber, header.number, this.#finalizedBlockSignal, 'finalized');
|
|
149
|
+
// Emit current block
|
|
150
|
+
const blockInfo = this.toBlockInfo(header);
|
|
151
|
+
this.#finalizedBlockSignal.next(blockInfo);
|
|
152
|
+
})
|
|
153
|
+
.catch((error) => {
|
|
154
|
+
console.error('Error processing finalized block:', error);
|
|
155
|
+
});
|
|
156
|
+
})
|
|
157
|
+
.then((rpcUnsub) => {
|
|
158
|
+
unsub = rpcUnsub;
|
|
159
|
+
});
|
|
160
|
+
// Immediately set unsub wrapper to prevent race conditions
|
|
161
|
+
this.#finalizedBlockUnsub = () => {
|
|
162
|
+
done = true;
|
|
163
|
+
blockQueue.cancel();
|
|
164
|
+
unsub && unsub().catch(utils_1.noop);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Clean up best block subscription when no more listeners
|
|
169
|
+
*/
|
|
170
|
+
cleanupBestBlockSubscription() {
|
|
171
|
+
if (this.#bestBlockSignal.listenerCount === 0 && this.#bestBlockUnsub) {
|
|
172
|
+
this.#bestBlockUnsub();
|
|
173
|
+
this.#bestBlockUnsub = undefined;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Clean up finalized block subscription when no more listeners
|
|
178
|
+
*/
|
|
179
|
+
cleanupFinalizedBlockSubscription() {
|
|
180
|
+
if (this.#finalizedBlockSignal.listenerCount === 0 && this.#finalizedBlockUnsub) {
|
|
181
|
+
this.#finalizedBlockUnsub();
|
|
182
|
+
this.#finalizedBlockUnsub = undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
calculateBlockHash(header) {
|
|
186
|
+
return this.#client.registry.hashAsHex(codecs_1.$Header.tryEncode(header));
|
|
187
|
+
}
|
|
188
|
+
best(callback) {
|
|
189
|
+
if (callback) {
|
|
190
|
+
// Subscribe mode - use shared subscription
|
|
191
|
+
// Ensure RPC subscription is active (creates on first listener)
|
|
192
|
+
this.ensureBestBlockSubscription();
|
|
193
|
+
// Subscribe to subject (automatically emits current value if available)
|
|
194
|
+
const unsub = this.#bestBlockSignal.subscribe(callback);
|
|
195
|
+
// Return cleanup function
|
|
196
|
+
return () => {
|
|
197
|
+
unsub();
|
|
198
|
+
this.cleanupBestBlockSubscription();
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// One-time query - return cached value if available
|
|
203
|
+
if (this.#bestBlockSignal.value) {
|
|
204
|
+
return Promise.resolve(this.#bestBlockSignal.value);
|
|
205
|
+
}
|
|
206
|
+
// Fallback to RPC call if no cached value
|
|
207
|
+
return this.#client.rpc.chain_getHeader().then(async (header) => {
|
|
208
|
+
(0, utils_1.assert)(header, 'Header not found');
|
|
209
|
+
return {
|
|
210
|
+
hash: this.calculateBlockHash(header),
|
|
211
|
+
number: header.number,
|
|
212
|
+
parent: header.parentHash,
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
finalized(callback) {
|
|
218
|
+
if (callback) {
|
|
219
|
+
// Subscribe mode - use shared subscription
|
|
220
|
+
// Ensure RPC subscription is active (creates on first listener)
|
|
221
|
+
this.ensureFinalizedBlockSubscription();
|
|
222
|
+
// Subscribe to subject (automatically emits current value if available)
|
|
223
|
+
const unsub = this.#finalizedBlockSignal.subscribe(callback);
|
|
224
|
+
// Return cleanup function
|
|
225
|
+
return () => {
|
|
226
|
+
unsub();
|
|
227
|
+
this.cleanupFinalizedBlockSubscription();
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// One-time query - return cached value if available
|
|
232
|
+
if (this.#finalizedBlockSignal.value) {
|
|
233
|
+
return Promise.resolve(this.#finalizedBlockSignal.value);
|
|
234
|
+
}
|
|
235
|
+
// Fallback to RPC call if no cached value
|
|
236
|
+
return this.#client.rpc.chain_getFinalizedHead().then(async (hash) => {
|
|
237
|
+
const header = await this.header(hash);
|
|
238
|
+
return {
|
|
239
|
+
hash,
|
|
240
|
+
number: header.number,
|
|
241
|
+
parent: header.parentHash,
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get the header of a block by number or hash
|
|
248
|
+
*/
|
|
249
|
+
async header(hash) {
|
|
250
|
+
const header = await this.#client.rpc.chain_getHeader(hash);
|
|
251
|
+
(0, utils_1.assert)(header, `Header not found for block ${hash}`);
|
|
252
|
+
return header;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get the body (transactions) of a block by number or hash
|
|
256
|
+
*/
|
|
257
|
+
async body(hash) {
|
|
258
|
+
const block = await this.#client.rpc.chain_getBlock(hash);
|
|
259
|
+
(0, utils_1.assert)(block, `Block not found for ${hash}`);
|
|
260
|
+
return block.block.extrinsics;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
exports.LegacyBlockExplorer = LegacyBlockExplorer;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.V2BlockExplorer = void 0;
|
|
4
|
+
const codecs_1 = require("@dedot/codecs");
|
|
5
|
+
const utils_1 = require("@dedot/utils");
|
|
6
|
+
/**
|
|
7
|
+
* @name V2BlockExplorer
|
|
8
|
+
* @description Block explorer implementation for V2Client using JSON-RPC v2 (ChainHead API)
|
|
9
|
+
*/
|
|
10
|
+
class V2BlockExplorer {
|
|
11
|
+
#chainHead;
|
|
12
|
+
#archive;
|
|
13
|
+
constructor(client) {
|
|
14
|
+
this.#chainHead = client.chainHead;
|
|
15
|
+
this.#archive = client.archive;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Convert PinnedBlock to BlockInfo format
|
|
19
|
+
*/
|
|
20
|
+
toBlockInfo(block) {
|
|
21
|
+
return {
|
|
22
|
+
hash: block.hash,
|
|
23
|
+
number: block.number,
|
|
24
|
+
parent: block.parent,
|
|
25
|
+
runtimeUpgraded: block.runtimeUpgraded === true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
best(callback) {
|
|
29
|
+
if (callback) {
|
|
30
|
+
// Subscribe mode
|
|
31
|
+
const handler = (block) => {
|
|
32
|
+
callback(this.toBlockInfo(block));
|
|
33
|
+
};
|
|
34
|
+
this.#chainHead.bestBlock().then((block) => {
|
|
35
|
+
handler(block);
|
|
36
|
+
});
|
|
37
|
+
return this.#chainHead.on('bestBlock', handler);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// One-time query
|
|
41
|
+
return this.#chainHead.bestBlock().then((block) => this.toBlockInfo(block));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
finalized(callback) {
|
|
45
|
+
if (callback) {
|
|
46
|
+
// Subscribe mode
|
|
47
|
+
const handler = (block) => {
|
|
48
|
+
callback(this.toBlockInfo(block));
|
|
49
|
+
};
|
|
50
|
+
this.#chainHead.finalizedBlock().then((block) => {
|
|
51
|
+
handler(block);
|
|
52
|
+
});
|
|
53
|
+
return this.#chainHead.on('finalizedBlock', handler);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return this.#chainHead.finalizedBlock().then((block) => this.toBlockInfo(block));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get the header of a block by number or hash
|
|
61
|
+
*/
|
|
62
|
+
async header(hash) {
|
|
63
|
+
const rawHeader = await this.#chainHead.header(hash);
|
|
64
|
+
(0, utils_1.assert)(rawHeader, `Header not found for block ${hash}`);
|
|
65
|
+
return codecs_1.$Header.tryDecode(rawHeader);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the body (transactions) of a block by number or hash
|
|
69
|
+
*/
|
|
70
|
+
async body(hash) {
|
|
71
|
+
return await this.#chainHead.body(hash);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.V2BlockExplorer = V2BlockExplorer;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./V2BlockExplorer.js"), exports);
|
|
18
|
+
__exportStar(require("./LegacyBlockExplorer.js"), exports);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectHasherFromHeader = exports.detectHasherFromMetadata = void 0;
|
|
4
|
+
const codecs_1 = require("@dedot/codecs");
|
|
5
|
+
const utils_1 = require("@dedot/utils");
|
|
6
|
+
const HASHERS = {
|
|
7
|
+
BlakeTwo256: utils_1.blake2_256, // commonly used in polkadot-sdk based-chains
|
|
8
|
+
Keccak256: utils_1.keccak_256, // widely used in EVM-compatible chains, e.g: Hyperbridge
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Detects the hasher from V16 metadata by looking at the System pallet's Hashing associated type
|
|
12
|
+
*
|
|
13
|
+
* @param metadata - The metadata to inspect
|
|
14
|
+
* @returns The detected HashFn or undefined if detection fails
|
|
15
|
+
*/
|
|
16
|
+
function detectHasherFromMetadata(metadata) {
|
|
17
|
+
try {
|
|
18
|
+
const latest = metadata.latest;
|
|
19
|
+
// Find the System pallet
|
|
20
|
+
const systemPallet = latest.pallets.find((p) => p.name === 'System');
|
|
21
|
+
if (!systemPallet)
|
|
22
|
+
return undefined;
|
|
23
|
+
// Find the Hashing associated type
|
|
24
|
+
const hashingType = systemPallet.associatedTypes.find((t) => t.name === 'Hashing');
|
|
25
|
+
if (!hashingType)
|
|
26
|
+
return undefined;
|
|
27
|
+
// Get the type definition
|
|
28
|
+
const typeDef = latest.types[hashingType.typeId];
|
|
29
|
+
if (!typeDef)
|
|
30
|
+
return undefined;
|
|
31
|
+
// Get the hasher name from the type path (last element)
|
|
32
|
+
const hasherName = typeDef.path.at(-1);
|
|
33
|
+
if (!hasherName)
|
|
34
|
+
return undefined;
|
|
35
|
+
return HASHERS[hasherName];
|
|
36
|
+
}
|
|
37
|
+
catch { }
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
exports.detectHasherFromMetadata = detectHasherFromMetadata;
|
|
41
|
+
/**
|
|
42
|
+
* Detects the hasher by trying different hash functions on a block header
|
|
43
|
+
* and comparing the result with the expected block hash
|
|
44
|
+
*
|
|
45
|
+
* @param header - The block header to hash
|
|
46
|
+
* @param expectedHash - The expected block hash
|
|
47
|
+
* @returns The matching HashFn or undefined if no match is found
|
|
48
|
+
*/
|
|
49
|
+
function detectHasherFromHeader(header, expectedHash) {
|
|
50
|
+
try {
|
|
51
|
+
const encodedHeader = codecs_1.$Header.tryEncode(header);
|
|
52
|
+
return Object.values(HASHERS).find((h) => (0, utils_1.u8aToHex)(h(encodedHeader)) === expectedHash);
|
|
53
|
+
}
|
|
54
|
+
catch { }
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
exports.detectHasherFromHeader = detectHasherFromHeader;
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.RuntimeApiExecutor = exports.FallbackRuntimeApiSpecs = exports.FallbackRuntimeApis = void 0;
|
|
4
27
|
const runtime_specs_1 = require("@dedot/runtime-specs");
|
|
28
|
+
const $ = __importStar(require("@dedot/shape"));
|
|
5
29
|
const utils_1 = require("@dedot/utils");
|
|
6
30
|
const Executor_js_1 = require("./Executor.js");
|
|
31
|
+
const validation_helpers_js_1 = require("./validation-helpers.js");
|
|
7
32
|
exports.FallbackRuntimeApis = { '0x37e397fc7c91f5e4': 2 };
|
|
8
33
|
exports.FallbackRuntimeApiSpecs = { Metadata: runtime_specs_1.Metadata };
|
|
9
34
|
/**
|
|
@@ -22,8 +47,30 @@ class RuntimeApiExecutor extends Executor_js_1.Executor {
|
|
|
22
47
|
(0, utils_1.assert)(callSpec, new utils_1.UnknownApiError(`Runtime api spec not found for ${callName}`));
|
|
23
48
|
const callFn = async (...args) => {
|
|
24
49
|
const { params } = callSpec;
|
|
25
|
-
|
|
26
|
-
const
|
|
50
|
+
// Pad args with undefined for missing trailing optional parameters
|
|
51
|
+
const withCodecs = params.every((p) => !!p.codec);
|
|
52
|
+
const paddedArgs = (0, validation_helpers_js_1.padArgsForOptionalParams)(args, params, withCodecs
|
|
53
|
+
? {} // intentional trick to pass-by client initialization
|
|
54
|
+
: this.registry);
|
|
55
|
+
const $ParamsTuple = $.Tuple(...params.map((param) => this.#findCodec(param, // --
|
|
56
|
+
`Codec not found for param ${param.name}`)));
|
|
57
|
+
// Enhanced validation with detailed error messages
|
|
58
|
+
try {
|
|
59
|
+
$ParamsTuple.assert?.(paddedArgs);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// Enhance Shape assertion errors with detailed compatibility information
|
|
63
|
+
if (error instanceof $.ShapeAssertError) {
|
|
64
|
+
throw (0, validation_helpers_js_1.buildCompatibilityError)(error, // --
|
|
65
|
+
params, args, {
|
|
66
|
+
apiName: `${runtimeApi}.${method}`,
|
|
67
|
+
registry: this.registry,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
const formattedInputs = $ParamsTuple.tryEncode(paddedArgs);
|
|
73
|
+
const bytes = (0, utils_1.u8aToHex)(formattedInputs);
|
|
27
74
|
const callParams = {
|
|
28
75
|
func: callName,
|
|
29
76
|
params: bytes,
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.StorageQueryExecutor = void 0;
|
|
27
|
+
const $ = __importStar(require("@dedot/shape"));
|
|
4
28
|
const utils_1 = require("@dedot/utils");
|
|
5
29
|
const index_js_1 = require("../storage/index.js");
|
|
6
30
|
const Executor_js_1 = require("./Executor.js");
|
|
31
|
+
const validation_helpers_js_1 = require("./validation-helpers.js");
|
|
7
32
|
const DEFAULT_KEYS_PAGE_SIZE = 1000;
|
|
8
33
|
const DEFAULT_ENTRIES_PAGE_SIZE = 250;
|
|
9
34
|
/**
|
|
@@ -32,6 +57,8 @@ class StorageQueryExecutor extends Executor_js_1.Executor {
|
|
|
32
57
|
};
|
|
33
58
|
const queryFn = async (...args) => {
|
|
34
59
|
const [inArgs, callback] = extractArgs(args);
|
|
60
|
+
// Validate storage inputs before encoding
|
|
61
|
+
this.#validateStorageInputs(entry, inArgs.at(0));
|
|
35
62
|
const encodedKey = entry.encodeKey(inArgs.at(0));
|
|
36
63
|
// if a callback is passed, make a storage subscription and return an unsub function
|
|
37
64
|
if (callback) {
|
|
@@ -58,6 +85,10 @@ class StorageQueryExecutor extends Executor_js_1.Executor {
|
|
|
58
85
|
const [inArgs, callback] = extractArgs(args);
|
|
59
86
|
const multiArgs = inArgs.at(0);
|
|
60
87
|
(0, utils_1.assert)(Array.isArray(multiArgs), 'First param for multi query should be an array');
|
|
88
|
+
// Validate each argument before encoding
|
|
89
|
+
multiArgs.forEach((arg, index) => {
|
|
90
|
+
this.#validateStorageInputs(entry, arg, index);
|
|
91
|
+
});
|
|
61
92
|
const encodedKeys = multiArgs.map((arg) => entry.encodeKey(arg));
|
|
62
93
|
// if a callback is passed, make a storage subscription and return an unsub function
|
|
63
94
|
if (callback) {
|
|
@@ -79,7 +110,7 @@ class StorageQueryExecutor extends Executor_js_1.Executor {
|
|
|
79
110
|
exposeStorageMapMethods(entry) {
|
|
80
111
|
const rawKeys = async (partialInput, pagination) => {
|
|
81
112
|
const pageSize = pagination?.pageSize || DEFAULT_KEYS_PAGE_SIZE;
|
|
82
|
-
const startKey = pagination?.startKey
|
|
113
|
+
const startKey = pagination?.startKey;
|
|
83
114
|
return await this.client.rpc.state_getKeysPaged(entry.encodeKey(partialInput, true), pageSize, startKey, this.atBlockHash);
|
|
84
115
|
};
|
|
85
116
|
const extractArgs = (args) => {
|
|
@@ -99,7 +130,88 @@ class StorageQueryExecutor extends Executor_js_1.Executor {
|
|
|
99
130
|
const storageMap = await this.queryStorage(storageKeys, this.atBlockHash);
|
|
100
131
|
return storageKeys.map((key) => [entry.decodeKey(key), entry.decodeValue(storageMap[key])]);
|
|
101
132
|
};
|
|
102
|
-
|
|
133
|
+
const entries = async (...args) => {
|
|
134
|
+
const [inArgs] = extractArgs(args);
|
|
135
|
+
const allEntries = [];
|
|
136
|
+
let startKey;
|
|
137
|
+
while (true) {
|
|
138
|
+
const pagination = {
|
|
139
|
+
pageSize: DEFAULT_ENTRIES_PAGE_SIZE,
|
|
140
|
+
...(startKey ? { startKey } : {}),
|
|
141
|
+
};
|
|
142
|
+
const storageKeys = await rawKeys(inArgs, pagination);
|
|
143
|
+
if (storageKeys.length === 0)
|
|
144
|
+
break;
|
|
145
|
+
const storageMap = await this.queryStorage(storageKeys, this.atBlockHash);
|
|
146
|
+
const pageEntries = storageKeys.map((key) => [
|
|
147
|
+
entry.decodeKey(key),
|
|
148
|
+
entry.decodeValue(storageMap[key]),
|
|
149
|
+
]);
|
|
150
|
+
allEntries.push(...pageEntries);
|
|
151
|
+
if (storageKeys.length < DEFAULT_ENTRIES_PAGE_SIZE)
|
|
152
|
+
break;
|
|
153
|
+
startKey = storageKeys[storageKeys.length - 1];
|
|
154
|
+
}
|
|
155
|
+
return allEntries;
|
|
156
|
+
};
|
|
157
|
+
return { entries, pagedKeys, pagedEntries };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate storage query inputs before encoding
|
|
161
|
+
* @param entry - QueryableStorage instance
|
|
162
|
+
* @param keyInput - Input keys to validate
|
|
163
|
+
* @param itemIndex - Optional index for multi query error messages
|
|
164
|
+
*/
|
|
165
|
+
#validateStorageInputs(entry, keyInput, itemIndex) {
|
|
166
|
+
const { storageType } = entry.storageEntry;
|
|
167
|
+
// Plain storage doesn't require validation
|
|
168
|
+
if (storageType.type === 'Plain') {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (storageType.type === 'Map') {
|
|
172
|
+
const { hashers, keyTypeId } = storageType.value;
|
|
173
|
+
const requiredKeys = hashers.length;
|
|
174
|
+
// Extract keyTypeIds for multi-key storage
|
|
175
|
+
let keyTypeIds = [keyTypeId];
|
|
176
|
+
if (hashers.length > 1) {
|
|
177
|
+
const { typeDef } = this.registry.findType(keyTypeId);
|
|
178
|
+
(0, utils_1.assert)(typeDef.type === 'Tuple', 'Key type should be a tuple!');
|
|
179
|
+
keyTypeIds = typeDef.value.fields;
|
|
180
|
+
}
|
|
181
|
+
// Normalize input to array format
|
|
182
|
+
let keys;
|
|
183
|
+
if (requiredKeys === 1) {
|
|
184
|
+
// Single key: accept both single value or array
|
|
185
|
+
keys = Array.isArray(keyInput) ? keyInput : [keyInput];
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// Multiple keys: must be array
|
|
189
|
+
(0, utils_1.assert)(Array.isArray(keyInput), 'Multi-key storage requires array input');
|
|
190
|
+
keys = keyInput;
|
|
191
|
+
}
|
|
192
|
+
// Build parameter specs for error messages
|
|
193
|
+
const paramSpecs = keyTypeIds.map((typeId, index) => ({
|
|
194
|
+
name: `key${index}`,
|
|
195
|
+
typeId,
|
|
196
|
+
}));
|
|
197
|
+
// Create tuple codec for validation
|
|
198
|
+
const $ParamsTuple = $.Tuple(...keyTypeIds.map((id) => this.registry.findCodec(id)));
|
|
199
|
+
// Validate inputs
|
|
200
|
+
try {
|
|
201
|
+
$ParamsTuple.assert?.(keys);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
if (error instanceof $.ShapeAssertError) {
|
|
205
|
+
throw (0, validation_helpers_js_1.buildCompatibilityError)(error, // -
|
|
206
|
+
paramSpecs, keys, {
|
|
207
|
+
apiName: `${(0, utils_1.stringCamelCase)(entry.pallet.name)}.${(0, utils_1.stringCamelCase)(entry.storageEntry.name)}`,
|
|
208
|
+
registry: this.registry,
|
|
209
|
+
itemIndex,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
103
215
|
}
|
|
104
216
|
getStorageQuery() {
|
|
105
217
|
return new index_js_1.LegacyStorageQuery(this.client);
|
|
@@ -27,6 +27,7 @@ exports.ViewFunctionExecutor = void 0;
|
|
|
27
27
|
const $ = __importStar(require("@dedot/shape"));
|
|
28
28
|
const utils_1 = require("@dedot/utils");
|
|
29
29
|
const Executor_js_1 = require("./Executor.js");
|
|
30
|
+
const validation_helpers_js_1 = require("./validation-helpers.js");
|
|
30
31
|
const RUNTIME_API_NAME = 'RuntimeViewFunction';
|
|
31
32
|
const METHOD_NAME = 'execute_view_function';
|
|
32
33
|
/**
|
|
@@ -40,8 +41,26 @@ class ViewFunctionExecutor extends Executor_js_1.Executor {
|
|
|
40
41
|
(0, utils_1.assert)(viewFunctionDef, new utils_1.UnknownApiError(`View function not found: ${pallet}.${viewFunction}`));
|
|
41
42
|
const callFn = async (...args) => {
|
|
42
43
|
const { inputs, id, output } = viewFunctionDef;
|
|
43
|
-
|
|
44
|
-
const
|
|
44
|
+
// Pad args with undefined for missing trailing optional parameters
|
|
45
|
+
const paddedArgs = (0, validation_helpers_js_1.padArgsForOptionalParams)(args, inputs, this.registry);
|
|
46
|
+
const $ParamsTuple = $.Tuple(...inputs.map((param) => this.registry.findCodec(param.typeId)));
|
|
47
|
+
// Enhanced validation with detailed error messages
|
|
48
|
+
try {
|
|
49
|
+
$ParamsTuple.assert?.(paddedArgs);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// Enhance Shape assertion errors with detailed compatibility information
|
|
53
|
+
if (error instanceof $.ShapeAssertError) {
|
|
54
|
+
throw (0, validation_helpers_js_1.buildCompatibilityError)(error, // --
|
|
55
|
+
inputs, args, {
|
|
56
|
+
apiName: `${pallet}.${viewFunction}`,
|
|
57
|
+
registry: this.registry,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
const formattedInputs = $ParamsTuple.tryEncode(paddedArgs);
|
|
63
|
+
const bytes = (0, utils_1.u8aToHex)((0, utils_1.concatU8a)(id, $.Vec($.u8).tryEncode(formattedInputs)));
|
|
45
64
|
const func = `${RUNTIME_API_NAME}_${METHOD_NAME}`;
|
|
46
65
|
const callParams = {
|
|
47
66
|
func,
|