@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
|
@@ -4,9 +4,12 @@ exports.LegacyClient = void 0;
|
|
|
4
4
|
const codecs_1 = require("@dedot/codecs");
|
|
5
5
|
const utils_1 = require("@dedot/utils");
|
|
6
6
|
const index_js_1 = require("../executor/index.js");
|
|
7
|
+
const SubmittableExtrinsic_js_1 = require("../extrinsic/submittable/SubmittableExtrinsic.js");
|
|
7
8
|
const proxychain_js_1 = require("../proxychain.js");
|
|
8
9
|
const index_js_2 = require("../storage/index.js");
|
|
9
10
|
const BaseSubstrateClient_js_1 = require("./BaseSubstrateClient.js");
|
|
11
|
+
const index_js_3 = require("./explorer/index.js");
|
|
12
|
+
const utils_js_1 = require("./utils.js");
|
|
10
13
|
const KEEP_ALIVE_INTERVAL = 10_000; // in ms
|
|
11
14
|
/**
|
|
12
15
|
* @name LegacyClient
|
|
@@ -50,6 +53,8 @@ class LegacyClient// prettier-end-here
|
|
|
50
53
|
extends BaseSubstrateClient_js_1.BaseSubstrateClient {
|
|
51
54
|
#runtimeSubscriptionUnsub;
|
|
52
55
|
#healthTimer;
|
|
56
|
+
#hasher;
|
|
57
|
+
_blockExplorer;
|
|
53
58
|
/**
|
|
54
59
|
* Use factory methods (`create`, `new`) to create `Dedot` instances.
|
|
55
60
|
*
|
|
@@ -74,9 +79,6 @@ class LegacyClient// prettier-end-here
|
|
|
74
79
|
static async new(options) {
|
|
75
80
|
return LegacyClient.create(options);
|
|
76
81
|
}
|
|
77
|
-
onDisconnected = async () => {
|
|
78
|
-
await this.#unsubscribeUpdates();
|
|
79
|
-
};
|
|
80
82
|
async beforeDisconnect() {
|
|
81
83
|
await this.#unsubscribeUpdates();
|
|
82
84
|
}
|
|
@@ -84,46 +86,91 @@ class LegacyClient// prettier-end-here
|
|
|
84
86
|
* Initialize APIs before usage
|
|
85
87
|
*/
|
|
86
88
|
async doInitialize() {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
// Determine if this is the first initialization
|
|
90
|
+
const shouldInitialize = !this._genesisHash;
|
|
91
|
+
if (shouldInitialize) {
|
|
92
|
+
// First load: Run all requests in parallel for speed
|
|
93
|
+
let [genesisHash, runtimeVersion, metadata] = await Promise.all([
|
|
94
|
+
this.rpc.chain_getBlockHash(0),
|
|
95
|
+
this.#getRuntimeVersion(),
|
|
96
|
+
(await this.shouldPreloadMetadata()) ? this.fetchMetadata() : Promise.resolve(undefined),
|
|
97
|
+
]);
|
|
98
|
+
this._genesisHash = genesisHash;
|
|
99
|
+
this._runtimeVersion = runtimeVersion;
|
|
100
|
+
await this.setupMetadata(metadata);
|
|
101
|
+
// Initialize block explorer
|
|
102
|
+
this._blockExplorer = new index_js_3.LegacyBlockExplorer(this);
|
|
103
|
+
// Subscribe to updates
|
|
104
|
+
this.#subscribeUpdates();
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// Reconnect: Only fetch runtime version to check for changes
|
|
108
|
+
const newRuntimeVersion = await this.#getRuntimeVersion();
|
|
109
|
+
// Only fetch metadata if spec version has changed
|
|
110
|
+
if (newRuntimeVersion.specVersion !== this._runtimeVersion?.specVersion) {
|
|
111
|
+
this._runtimeVersion = newRuntimeVersion;
|
|
112
|
+
const metadata = await this.fetchMetadata();
|
|
113
|
+
await this.setupMetadata(metadata);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
96
116
|
}
|
|
97
117
|
cleanUp() {
|
|
98
118
|
super.cleanUp();
|
|
99
119
|
this.#healthTimer = undefined;
|
|
100
120
|
this.#runtimeSubscriptionUnsub = undefined;
|
|
121
|
+
this._blockExplorer = undefined;
|
|
101
122
|
}
|
|
102
123
|
#subscribeRuntimeUpgrades() {
|
|
103
124
|
if (this.#runtimeSubscriptionUnsub)
|
|
104
125
|
return;
|
|
105
|
-
this.
|
|
106
|
-
|
|
126
|
+
this.#runtimeSubscriptionUnsub = this.block.best(async (block) => {
|
|
127
|
+
if (!block.runtimeUpgraded)
|
|
128
|
+
return;
|
|
129
|
+
const { hash } = block;
|
|
130
|
+
// Fetch the runtime version at this block
|
|
131
|
+
const runtimeVersion = await this.#getRuntimeVersion(hash);
|
|
132
|
+
// Check if the spec version has actually changed
|
|
107
133
|
if (runtimeVersion.specVersion !== this.runtimeVersion?.specVersion) {
|
|
108
134
|
this.startRuntimeUpgrade();
|
|
109
|
-
this._runtimeVersion =
|
|
110
|
-
const newMetadata = await this.fetchMetadata(
|
|
135
|
+
this._runtimeVersion = runtimeVersion;
|
|
136
|
+
const newMetadata = await this.fetchMetadata(hash, this._runtimeVersion);
|
|
111
137
|
await this.setupMetadata(newMetadata);
|
|
112
|
-
this.emit('runtimeUpgraded', this._runtimeVersion);
|
|
138
|
+
this.emit('runtimeUpgraded', this._runtimeVersion, block);
|
|
113
139
|
this.doneRuntimeUpgrade();
|
|
114
140
|
}
|
|
115
|
-
})
|
|
116
|
-
.then((unsub) => {
|
|
117
|
-
this.#runtimeSubscriptionUnsub = unsub;
|
|
118
141
|
});
|
|
119
142
|
}
|
|
120
143
|
async #getRuntimeVersion(at) {
|
|
121
144
|
return this.toSubstrateRuntimeVersion(await this.rpc.state_getRuntimeVersion(at));
|
|
122
145
|
}
|
|
146
|
+
async setMetadata(metadata) {
|
|
147
|
+
this._metadata = metadata;
|
|
148
|
+
// Detect hasher if not provided by user
|
|
149
|
+
// Here we assume that hasher method of a chain is less likely to be changed via runtime upgrades
|
|
150
|
+
// So for now, we only need to detect it in initial connection and reuse it later
|
|
151
|
+
if (!this.#hasher && !this.options.hasher) {
|
|
152
|
+
// Try metadata first
|
|
153
|
+
this.#hasher = (0, utils_js_1.detectHasherFromMetadata)(metadata);
|
|
154
|
+
// Fallback to block header detection
|
|
155
|
+
if (!this.#hasher) {
|
|
156
|
+
try {
|
|
157
|
+
const hash = await this.rpc.chain_getFinalizedHead();
|
|
158
|
+
const header = await this.rpc.chain_getHeader(hash);
|
|
159
|
+
if (header) {
|
|
160
|
+
this.#hasher = (0, utils_js_1.detectHasherFromHeader)(header, hash);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Ignore errors in fallback detection
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
this._registry = new codecs_1.PortableRegistry(metadata.latest, this.#hasher || this.options.hasher);
|
|
169
|
+
}
|
|
123
170
|
#subscribeHealth() {
|
|
124
171
|
this.#unsubscribeHealth();
|
|
125
172
|
this.#healthTimer = setInterval(() => {
|
|
126
|
-
this.rpc.system_health().catch(
|
|
173
|
+
this.rpc.system_health().catch(utils_1.noop);
|
|
127
174
|
}, KEEP_ALIVE_INTERVAL);
|
|
128
175
|
}
|
|
129
176
|
#unsubscribeHealth() {
|
|
@@ -138,16 +185,23 @@ class LegacyClient// prettier-end-here
|
|
|
138
185
|
return;
|
|
139
186
|
}
|
|
140
187
|
try {
|
|
141
|
-
|
|
188
|
+
this.#runtimeSubscriptionUnsub();
|
|
142
189
|
this.#runtimeSubscriptionUnsub = undefined;
|
|
143
190
|
}
|
|
144
191
|
catch {
|
|
145
192
|
// ignore
|
|
146
193
|
}
|
|
147
194
|
}
|
|
195
|
+
#subscribeStalingDetection() {
|
|
196
|
+
const stalingDetectionFn = this.getStalingDetectionFn();
|
|
197
|
+
if (!stalingDetectionFn)
|
|
198
|
+
return;
|
|
199
|
+
this.block.best(stalingDetectionFn);
|
|
200
|
+
}
|
|
148
201
|
#subscribeUpdates() {
|
|
149
202
|
this.#subscribeRuntimeUpgrades();
|
|
150
203
|
this.#subscribeHealth();
|
|
204
|
+
this.#subscribeStalingDetection();
|
|
151
205
|
}
|
|
152
206
|
async #unsubscribeUpdates() {
|
|
153
207
|
await this.#unsubscribeRuntimeUpdates();
|
|
@@ -224,6 +278,22 @@ class LegacyClient// prettier-end-here
|
|
|
224
278
|
get tx() {
|
|
225
279
|
return (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.TxExecutor(this) });
|
|
226
280
|
}
|
|
281
|
+
get block() {
|
|
282
|
+
return (0, BaseSubstrateClient_js_1.ensurePresence)(this._blockExplorer);
|
|
283
|
+
}
|
|
284
|
+
get chainSpec() {
|
|
285
|
+
return {
|
|
286
|
+
chainName: () => {
|
|
287
|
+
return this.rpc.system_chain();
|
|
288
|
+
},
|
|
289
|
+
genesisHash: () => {
|
|
290
|
+
return Promise.resolve(this.genesisHash);
|
|
291
|
+
},
|
|
292
|
+
properties: () => {
|
|
293
|
+
return this.rpc.system_properties();
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
227
297
|
/**
|
|
228
298
|
* Create a new API instance at a specific block hash
|
|
229
299
|
* This is useful when we want to inspect the state of the chain at a specific block hash
|
|
@@ -246,7 +316,7 @@ class LegacyClient// prettier-end-here
|
|
|
246
316
|
}
|
|
247
317
|
else {
|
|
248
318
|
metadata = await this.fetchMetadata(parentHash, targetVersion);
|
|
249
|
-
registry = new codecs_1.PortableRegistry(metadata.latest, this.options.hasher);
|
|
319
|
+
registry = new codecs_1.PortableRegistry(metadata.latest, this.#hasher || this.options.hasher);
|
|
250
320
|
}
|
|
251
321
|
}
|
|
252
322
|
const api = {
|
|
@@ -284,5 +354,11 @@ class LegacyClient// prettier-end-here
|
|
|
284
354
|
return header.parentHash;
|
|
285
355
|
}
|
|
286
356
|
}
|
|
357
|
+
sendTx(tx, callback) {
|
|
358
|
+
return SubmittableExtrinsic_js_1.SubmittableExtrinsic.fromTx(this, tx) // --
|
|
359
|
+
.send((result) => {
|
|
360
|
+
callback && callback(result);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
287
363
|
}
|
|
288
364
|
exports.LegacyClient = LegacyClient;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.V2Client = void 0;
|
|
4
|
+
const codecs_1 = require("@dedot/codecs");
|
|
5
|
+
const shape_1 = require("@dedot/shape");
|
|
6
|
+
const utils_1 = require("@dedot/utils");
|
|
7
|
+
const index_js_1 = require("../executor/index.js");
|
|
8
|
+
const SubmittableExtrinsicV2_js_1 = require("../extrinsic/submittable/SubmittableExtrinsicV2.js");
|
|
9
|
+
const index_js_2 = require("../json-rpc/index.js");
|
|
10
|
+
const proxychain_js_1 = require("../proxychain.js");
|
|
11
|
+
const index_js_3 = require("../storage/index.js");
|
|
12
|
+
const BaseSubstrateClient_js_1 = require("./BaseSubstrateClient.js");
|
|
13
|
+
const index_js_4 = require("./explorer/index.js");
|
|
14
|
+
/**
|
|
15
|
+
* @name V2Client
|
|
16
|
+
* @description New promised-based API Client for Polkadot & Substrate based on JSON-RPC V2
|
|
17
|
+
*
|
|
18
|
+
* __Unstable, use with caution.__
|
|
19
|
+
*/
|
|
20
|
+
class V2Client// prettier-end-here
|
|
21
|
+
extends BaseSubstrateClient_js_1.BaseSubstrateClient {
|
|
22
|
+
_chainHead;
|
|
23
|
+
_chainSpec;
|
|
24
|
+
_archive;
|
|
25
|
+
_txBroadcaster;
|
|
26
|
+
_blockExplorer;
|
|
27
|
+
#hasher;
|
|
28
|
+
/**
|
|
29
|
+
* Use factory methods (`create`, `new`) to create `V2Client` instances.
|
|
30
|
+
*
|
|
31
|
+
* @param options
|
|
32
|
+
*/
|
|
33
|
+
constructor(options) {
|
|
34
|
+
super('v2', options);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Factory method to create a new V2Client instance
|
|
38
|
+
*
|
|
39
|
+
* @param options
|
|
40
|
+
*/
|
|
41
|
+
static async create(options) {
|
|
42
|
+
return new V2Client(options).connect();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Alias for __V2Client.create__
|
|
46
|
+
*
|
|
47
|
+
* @param options
|
|
48
|
+
*/
|
|
49
|
+
static async new(options) {
|
|
50
|
+
return V2Client.create(options);
|
|
51
|
+
}
|
|
52
|
+
get chainSpec() {
|
|
53
|
+
return (0, BaseSubstrateClient_js_1.ensurePresence)(this._chainSpec);
|
|
54
|
+
}
|
|
55
|
+
get chainHead() {
|
|
56
|
+
return (0, BaseSubstrateClient_js_1.ensurePresence)(this._chainHead);
|
|
57
|
+
}
|
|
58
|
+
get archive() {
|
|
59
|
+
return this._archive;
|
|
60
|
+
}
|
|
61
|
+
get txBroadcaster() {
|
|
62
|
+
this.chainHead; // Ensure chain head is initialized
|
|
63
|
+
(0, utils_1.assert)(this._txBroadcaster, 'JSON-RPC method to broadcast transactions is not supported by the server/node.');
|
|
64
|
+
return this._txBroadcaster;
|
|
65
|
+
}
|
|
66
|
+
async #initializeTxBroadcaster(rpcMethods) {
|
|
67
|
+
const tx = new index_js_2.Transaction(this, { rpcMethods });
|
|
68
|
+
if (await tx.supported())
|
|
69
|
+
return tx;
|
|
70
|
+
const txWatch = new index_js_2.TransactionWatch(this, { rpcMethods });
|
|
71
|
+
if (await txWatch.supported())
|
|
72
|
+
return txWatch;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Initialize APIs before usage
|
|
76
|
+
*/
|
|
77
|
+
async doInitialize() {
|
|
78
|
+
const shouldInitialize = !this._genesisHash;
|
|
79
|
+
if (shouldInitialize) {
|
|
80
|
+
const rpcMethods = (await this.rpc.rpc_methods()).methods;
|
|
81
|
+
this._chainHead = new index_js_2.ChainHead(this, { rpcMethods });
|
|
82
|
+
this._chainSpec = new index_js_2.ChainSpec(this, { rpcMethods });
|
|
83
|
+
// Always initialize Archive, but only set up fallback if supported
|
|
84
|
+
const archive = new index_js_2.Archive(this, { rpcMethods });
|
|
85
|
+
// Set up ChainHead with Archive fallback only if Archive is supported
|
|
86
|
+
if (await archive.supported()) {
|
|
87
|
+
this._archive = archive;
|
|
88
|
+
this._chainHead.withArchive(archive);
|
|
89
|
+
}
|
|
90
|
+
this._txBroadcaster = await this.#initializeTxBroadcaster(rpcMethods);
|
|
91
|
+
this._blockExplorer = new index_js_4.V2BlockExplorer(this);
|
|
92
|
+
}
|
|
93
|
+
// Fetching node information
|
|
94
|
+
let [_, genesisHash] = await Promise.all([
|
|
95
|
+
this.chainHead.follow(true),
|
|
96
|
+
shouldInitialize ? this.chainSpec.genesisHash().catch(() => undefined) : Promise.resolve(this._genesisHash),
|
|
97
|
+
]);
|
|
98
|
+
this._genesisHash = genesisHash || (await this.#getGenesisHashFallback());
|
|
99
|
+
const newBestRuntime = await this.chainHead.bestRuntimeVersion();
|
|
100
|
+
if (!this._runtimeVersion || newBestRuntime.specVersion !== this._runtimeVersion.specVersion) {
|
|
101
|
+
this._runtimeVersion = newBestRuntime;
|
|
102
|
+
let metadata;
|
|
103
|
+
if (await this.shouldPreloadMetadata()) {
|
|
104
|
+
metadata = await this.fetchMetadata();
|
|
105
|
+
}
|
|
106
|
+
await this.setupMetadata(metadata);
|
|
107
|
+
}
|
|
108
|
+
if (shouldInitialize) {
|
|
109
|
+
this.subscribeRuntimeUpgrades();
|
|
110
|
+
this.chainHead.on('newBlock', (...args) => this.emit('newBlock', ...args));
|
|
111
|
+
this.chainHead.on('bestBlock', (...args) => this.emit('bestBlock', ...args));
|
|
112
|
+
this.chainHead.on('finalizedBlock', (...args) => this.emit('finalizedBlock', ...args));
|
|
113
|
+
this.chainHead.on('bestChainChanged', (...args) => this.emit('bestChainChanged', ...args));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Ref: https://github.com/paritytech/polkadot-sdk/blob/bbd51ce867967f71657b901f1a956ad4f75d352e/substrate/frame/system/src/lib.rs#L909-L913
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
async #getGenesisHashFallback() {
|
|
121
|
+
const pallet = (0, utils_1.xxhashAsU8a)('System', 128);
|
|
122
|
+
const item = (0, utils_1.xxhashAsU8a)('BlockHash', 128);
|
|
123
|
+
const blockHeightAt0 = (0, utils_1.twox64Concat)(shape_1.u32.encode(0));
|
|
124
|
+
const key = (0, utils_1.u8aToHex)((0, utils_1.concatU8a)(pallet, item, blockHeightAt0));
|
|
125
|
+
const storageValue = await this.chainHead.storage([{ type: 'value', key }]);
|
|
126
|
+
const rawGenesisHash = storageValue.at(0)?.value;
|
|
127
|
+
(0, utils_1.assert)(rawGenesisHash, 'Genesis hash not found!');
|
|
128
|
+
// Here we assume that in most case the hash is stored as a H256
|
|
129
|
+
return codecs_1.$H256.tryDecode(rawGenesisHash);
|
|
130
|
+
}
|
|
131
|
+
subscribeRuntimeUpgrades() {
|
|
132
|
+
this.chainHead.on('bestBlock', this.onRuntimeUpgrade);
|
|
133
|
+
this.#subscribeStalingDetection();
|
|
134
|
+
}
|
|
135
|
+
onRuntimeUpgrade = async (block) => {
|
|
136
|
+
const runtimeUpgraded = block.runtime && block.runtime.specVersion !== this._runtimeVersion?.specVersion;
|
|
137
|
+
if (!runtimeUpgraded)
|
|
138
|
+
return;
|
|
139
|
+
this.startRuntimeUpgrade();
|
|
140
|
+
this._runtimeVersion = block.runtime;
|
|
141
|
+
const newMetadata = await this.fetchMetadata(undefined, this._runtimeVersion);
|
|
142
|
+
await this.setupMetadata(newMetadata);
|
|
143
|
+
const blockInfo = {
|
|
144
|
+
hash: block.hash,
|
|
145
|
+
number: block.number,
|
|
146
|
+
parent: block.parent,
|
|
147
|
+
runtimeUpgraded: block.runtimeUpgraded === true,
|
|
148
|
+
};
|
|
149
|
+
this.emit('runtimeUpgraded', this._runtimeVersion, blockInfo);
|
|
150
|
+
this.doneRuntimeUpgrade();
|
|
151
|
+
};
|
|
152
|
+
async setMetadata(metadata) {
|
|
153
|
+
this._metadata = metadata;
|
|
154
|
+
// Here we assume that hasher method of a chain is less likely to change via runtime upgrades
|
|
155
|
+
// So for now, we only need to detect it in initial connection and reuse it later
|
|
156
|
+
if (!this.#hasher && !this.options.hasher) {
|
|
157
|
+
this.#hasher = await this.chainHead.hasher();
|
|
158
|
+
}
|
|
159
|
+
this._registry = new codecs_1.PortableRegistry(metadata.latest, // --
|
|
160
|
+
this.#hasher || this.options.hasher);
|
|
161
|
+
}
|
|
162
|
+
#subscribeStalingDetection() {
|
|
163
|
+
const stalingDetectionFn = this.getStalingDetectionFn();
|
|
164
|
+
if (!stalingDetectionFn)
|
|
165
|
+
return;
|
|
166
|
+
this.chainHead.on('bestBlock', stalingDetectionFn);
|
|
167
|
+
}
|
|
168
|
+
async beforeDisconnect() {
|
|
169
|
+
await this.chainHead.unfollow();
|
|
170
|
+
}
|
|
171
|
+
cleanUp() {
|
|
172
|
+
super.cleanUp();
|
|
173
|
+
this._chainHead = undefined;
|
|
174
|
+
this._chainSpec = undefined;
|
|
175
|
+
this._archive = undefined;
|
|
176
|
+
this._txBroadcaster = undefined;
|
|
177
|
+
this._blockExplorer = undefined;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* @description Clear local cache, API at-block cache, and ChainHead cache
|
|
181
|
+
* @param keepMetadataCache Keep the metadata cache, only clear other caches.
|
|
182
|
+
*/
|
|
183
|
+
async clearCache(keepMetadataCache = false) {
|
|
184
|
+
await super.clearCache(keepMetadataCache);
|
|
185
|
+
this._chainHead?.clearCache();
|
|
186
|
+
}
|
|
187
|
+
get query() {
|
|
188
|
+
return (0, proxychain_js_1.newProxyChain)({
|
|
189
|
+
executor: new index_js_1.StorageQueryExecutorV2(this, this.chainHead),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
get view() {
|
|
193
|
+
return (0, proxychain_js_1.newProxyChain)({
|
|
194
|
+
executor: new index_js_1.ViewFunctionExecutorV2(this, this.chainHead),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
get call() {
|
|
198
|
+
return this.callAt();
|
|
199
|
+
}
|
|
200
|
+
callAt(blockHash) {
|
|
201
|
+
return (0, proxychain_js_1.newProxyChain)({
|
|
202
|
+
executor: new index_js_1.RuntimeApiExecutorV2(this, this.chainHead, blockHash),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
get tx() {
|
|
206
|
+
return (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.TxExecutorV2(this) });
|
|
207
|
+
}
|
|
208
|
+
get block() {
|
|
209
|
+
return (0, BaseSubstrateClient_js_1.ensurePresence)(this._blockExplorer);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get a new API instance at a specific block hash
|
|
213
|
+
* Supports both pinned blocks (via ChainHead) and historical blocks (via Archive fallback)
|
|
214
|
+
*
|
|
215
|
+
* @param hash
|
|
216
|
+
*/
|
|
217
|
+
async at(hash) {
|
|
218
|
+
const cached = this._apiAtCache.get(hash);
|
|
219
|
+
if (cached)
|
|
220
|
+
return cached;
|
|
221
|
+
let parentVersion;
|
|
222
|
+
let parentHash;
|
|
223
|
+
// Try to get block info from ChainHead first (for pinned blocks)
|
|
224
|
+
const targetBlock = this.chainHead.findBlock(hash);
|
|
225
|
+
if (targetBlock) {
|
|
226
|
+
if (hash === this.genesisHash) {
|
|
227
|
+
parentHash = hash;
|
|
228
|
+
parentVersion = targetBlock.runtime;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
parentHash = targetBlock.parent;
|
|
232
|
+
const parentBlock = this.chainHead.findBlock(parentHash);
|
|
233
|
+
parentVersion = parentBlock?.runtime;
|
|
234
|
+
}
|
|
235
|
+
// fallback to fetching on-chain runtime if we can't find it in the block
|
|
236
|
+
if (!parentVersion) {
|
|
237
|
+
parentVersion = this.toSubstrateRuntimeVersion(await this.callAt(parentHash).core.version());
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
// Block not pinned, try via Archive fallback if supported
|
|
242
|
+
if (this._archive && (await this._archive.supported())) {
|
|
243
|
+
try {
|
|
244
|
+
if (hash === this.genesisHash) {
|
|
245
|
+
parentHash = hash;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
const rawHeader = await this._archive.header(hash);
|
|
249
|
+
(0, utils_1.assert)(rawHeader, `Header for block ${hash} not found`);
|
|
250
|
+
const header = codecs_1.$Header.tryDecode(rawHeader);
|
|
251
|
+
parentHash = header.parentHash;
|
|
252
|
+
}
|
|
253
|
+
// Fetch runtime version via Archive
|
|
254
|
+
const runtimeRaw = await this._archive.call('Core_version', '0x', parentHash);
|
|
255
|
+
(0, utils_1.assert)(runtimeRaw, 'Runtime Version Not Found');
|
|
256
|
+
parentVersion = this.toSubstrateRuntimeVersion(codecs_1.$RuntimeVersion.tryDecode(runtimeRaw));
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
throw new utils_1.DedotError(`Unable to fetch runtime version for block ${hash}: ${error}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
throw new utils_1.DedotError('Block is not pinned and Archive JSON-RPC is not supported by the server/node!');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
let metadata = this.metadata;
|
|
267
|
+
let registry = this.registry;
|
|
268
|
+
if (parentVersion && parentVersion.specVersion !== this.runtimeVersion.specVersion) {
|
|
269
|
+
const cachedMetadata = this.findMetadataInCache(parentVersion.specVersion);
|
|
270
|
+
if (cachedMetadata) {
|
|
271
|
+
metadata = cachedMetadata[0];
|
|
272
|
+
registry = cachedMetadata[1];
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
metadata = await this.fetchMetadata(parentHash, parentVersion);
|
|
276
|
+
registry = new codecs_1.PortableRegistry(metadata.latest, this.#hasher || this.options.hasher);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const api = {
|
|
280
|
+
rpcVersion: 'v2',
|
|
281
|
+
atBlockHash: hash,
|
|
282
|
+
options: this.options,
|
|
283
|
+
genesisHash: this.genesisHash,
|
|
284
|
+
runtimeVersion: parentVersion,
|
|
285
|
+
metadata,
|
|
286
|
+
registry,
|
|
287
|
+
rpc: this.rpc,
|
|
288
|
+
};
|
|
289
|
+
api.consts = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.ConstantExecutor(api) });
|
|
290
|
+
api.events = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.EventExecutor(api) });
|
|
291
|
+
api.errors = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.ErrorExecutor(api) });
|
|
292
|
+
api.query = (0, proxychain_js_1.newProxyChain)({
|
|
293
|
+
executor: new index_js_1.StorageQueryExecutorV2(api, this.chainHead),
|
|
294
|
+
});
|
|
295
|
+
api.call = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.RuntimeApiExecutorV2(api, this.chainHead) });
|
|
296
|
+
api.view = (0, proxychain_js_1.newProxyChain)({
|
|
297
|
+
executor: new index_js_1.ViewFunctionExecutorV2(api, this.chainHead),
|
|
298
|
+
});
|
|
299
|
+
// @ts-ignore Add queryMulti implementation for at-block queries
|
|
300
|
+
api.queryMulti = (queries) => {
|
|
301
|
+
return this.internalQueryMulti(queries, undefined, hash);
|
|
302
|
+
};
|
|
303
|
+
this._apiAtCache.set(hash, api);
|
|
304
|
+
return api;
|
|
305
|
+
}
|
|
306
|
+
getStorageQuery() {
|
|
307
|
+
return new index_js_3.NewStorageQuery(this);
|
|
308
|
+
}
|
|
309
|
+
sendTx(tx, callback) {
|
|
310
|
+
return SubmittableExtrinsicV2_js_1.SubmittableExtrinsicV2.fromTx(this, tx) // --
|
|
311
|
+
.send((result) => {
|
|
312
|
+
callback && callback(result);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
exports.V2Client = V2Client;
|