@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.
Files changed (102) hide show
  1. package/chaintypes/substrate/consts.d.ts +2 -2
  2. package/chaintypes/substrate/consts.js +1 -0
  3. package/chaintypes/substrate/errors.d.ts +909 -909
  4. package/chaintypes/substrate/errors.js +1 -0
  5. package/chaintypes/substrate/events.d.ts +664 -664
  6. package/chaintypes/substrate/events.js +1 -0
  7. package/chaintypes/substrate/index.d.ts +13 -16
  8. package/chaintypes/substrate/index.js +1 -0
  9. package/chaintypes/substrate/json-rpc.d.ts +2 -2
  10. package/chaintypes/substrate/json-rpc.js +1 -0
  11. package/chaintypes/substrate/query.d.ts +509 -527
  12. package/chaintypes/substrate/query.js +1 -0
  13. package/chaintypes/substrate/runtime.d.ts +123 -123
  14. package/chaintypes/substrate/runtime.js +1 -0
  15. package/chaintypes/substrate/tx.d.ts +1311 -1311
  16. package/chaintypes/substrate/tx.js +1 -0
  17. package/chaintypes/substrate/types.js +1 -0
  18. package/chaintypes/substrate/view-functions.d.ts +2 -2
  19. package/chaintypes/substrate/view-functions.js +1 -0
  20. package/cjs/chaintypes/substrate/consts.js +1 -0
  21. package/cjs/chaintypes/substrate/errors.js +1 -0
  22. package/cjs/chaintypes/substrate/events.js +1 -0
  23. package/cjs/chaintypes/substrate/index.js +1 -0
  24. package/cjs/chaintypes/substrate/json-rpc.js +1 -0
  25. package/cjs/chaintypes/substrate/query.js +1 -0
  26. package/cjs/chaintypes/substrate/runtime.js +1 -0
  27. package/cjs/chaintypes/substrate/tx.js +1 -0
  28. package/cjs/chaintypes/substrate/types.js +1 -0
  29. package/cjs/chaintypes/substrate/view-functions.js +1 -0
  30. package/cjs/client/BaseSubstrateClient.js +45 -2
  31. package/cjs/client/DedotClient.js +356 -224
  32. package/cjs/client/LegacyClient.js +99 -23
  33. package/cjs/client/V2Client.js +316 -0
  34. package/cjs/client/explorer/LegacyBlockExplorer.js +263 -0
  35. package/cjs/client/explorer/V2BlockExplorer.js +74 -0
  36. package/cjs/client/explorer/index.js +18 -0
  37. package/cjs/client/utils.js +57 -0
  38. package/cjs/executor/EventExecutor.js +1 -0
  39. package/cjs/executor/RuntimeApiExecutor.js +49 -2
  40. package/cjs/executor/StorageQueryExecutor.js +114 -2
  41. package/cjs/executor/ViewFunctionExecutor.js +21 -2
  42. package/cjs/executor/v2/StorageQueryExecutorV2.js +7 -1
  43. package/cjs/executor/validation-helpers.js +181 -0
  44. package/cjs/extrinsic/submittable/BaseSubmittableExtrinsic.js +86 -2
  45. package/cjs/extrinsic/submittable/SubmittableExtrinsic.js +38 -16
  46. package/cjs/extrinsic/submittable/SubmittableExtrinsicV2.js +12 -2
  47. package/cjs/json-rpc/group/ChainHead/ChainHead.js +226 -21
  48. package/cjs/storage/QueryableStorage.js +1 -0
  49. package/client/BaseSubstrateClient.d.ts +34 -18
  50. package/client/BaseSubstrateClient.js +47 -4
  51. package/client/DedotClient.d.ts +346 -49
  52. package/client/DedotClient.js +356 -224
  53. package/client/LegacyClient.d.ts +22 -17
  54. package/client/LegacyClient.js +101 -25
  55. package/client/V2Client.d.ts +75 -0
  56. package/client/V2Client.js +312 -0
  57. package/client/explorer/LegacyBlockExplorer.d.ts +68 -0
  58. package/client/explorer/LegacyBlockExplorer.js +259 -0
  59. package/client/explorer/V2BlockExplorer.d.ts +40 -0
  60. package/client/explorer/V2BlockExplorer.js +70 -0
  61. package/client/explorer/index.d.ts +2 -0
  62. package/client/explorer/index.js +2 -0
  63. package/client/utils.d.ts +18 -0
  64. package/client/utils.js +52 -0
  65. package/executor/ConstantExecutor.d.ts +1 -2
  66. package/executor/ErrorExecutor.d.ts +2 -2
  67. package/executor/EventExecutor.d.ts +2 -2
  68. package/executor/EventExecutor.js +1 -0
  69. package/executor/Executor.d.ts +4 -5
  70. package/executor/RuntimeApiExecutor.d.ts +2 -2
  71. package/executor/RuntimeApiExecutor.js +27 -3
  72. package/executor/StorageQueryExecutor.d.ts +3 -2
  73. package/executor/StorageQueryExecutor.js +92 -3
  74. package/executor/TxExecutor.d.ts +2 -2
  75. package/executor/ViewFunctionExecutor.d.ts +2 -2
  76. package/executor/ViewFunctionExecutor.js +22 -3
  77. package/executor/v2/RuntimeApiExecutorV2.d.ts +2 -3
  78. package/executor/v2/StorageQueryExecutorV2.d.ts +3 -3
  79. package/executor/v2/StorageQueryExecutorV2.js +8 -2
  80. package/executor/v2/TxExecutorV2.d.ts +5 -5
  81. package/executor/v2/ViewFunctionExecutorV2.d.ts +2 -3
  82. package/executor/validation-helpers.d.ts +36 -0
  83. package/executor/validation-helpers.js +174 -0
  84. package/extrinsic/extensions/SignedExtension.d.ts +1 -1
  85. package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +7 -3
  86. package/extrinsic/submittable/BaseSubmittableExtrinsic.js +64 -3
  87. package/extrinsic/submittable/SubmittableExtrinsic.d.ts +4 -1
  88. package/extrinsic/submittable/SubmittableExtrinsic.js +39 -17
  89. package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +6 -3
  90. package/extrinsic/submittable/SubmittableExtrinsicV2.js +13 -3
  91. package/json-rpc/JsonRpcClient.d.ts +6 -6
  92. package/json-rpc/group/Archive.d.ts +1 -1
  93. package/json-rpc/group/ChainHead/ChainHead.d.ts +18 -2
  94. package/json-rpc/group/ChainHead/ChainHead.js +226 -21
  95. package/json-rpc/group/ChainSpec.d.ts +3 -3
  96. package/json-rpc/group/Transaction.d.ts +1 -1
  97. package/json-rpc/group/TransactionWatch.d.ts +1 -1
  98. package/package.json +9 -9
  99. package/proxychain.d.ts +3 -4
  100. package/storage/NewStorageQuery.d.ts +3 -3
  101. package/storage/QueryableStorage.js +1 -0
  102. 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
- let [genesisHash, runtimeVersion, metadata] = await Promise.all([
88
- this.rpc.chain_getBlockHash(0),
89
- this.#getRuntimeVersion(),
90
- (await this.shouldPreloadMetadata()) ? this.fetchMetadata() : Promise.resolve(undefined),
91
- ]);
92
- this._genesisHash = genesisHash;
93
- this._runtimeVersion = runtimeVersion;
94
- await this.setupMetadata(metadata);
95
- this.#subscribeUpdates();
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.rpc
106
- .state_subscribeRuntimeVersion(async (runtimeVersion) => {
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 = this.toSubstrateRuntimeVersion(runtimeVersion);
110
- const newMetadata = await this.fetchMetadata(undefined, this._runtimeVersion);
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(console.error);
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
- await this.#runtimeSubscriptionUnsub();
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;