@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
@@ -1,270 +1,402 @@
1
- import { $H256, $Header, $RuntimeVersion, PortableRegistry } from '@dedot/codecs';
2
- import { u32 } from '@dedot/shape';
3
- import { assert, concatU8a, DedotError, noop, twox64Concat, u8aToHex, xxhashAsU8a } from '@dedot/utils';
4
- import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutorV2, StorageQueryExecutorV2, TxExecutorV2, ViewFunctionExecutorV2, } from '../executor/index.js';
5
- import { Archive, ChainHead, ChainSpec, Transaction, TransactionWatch } from '../json-rpc/index.js';
6
- import { newProxyChain } from '../proxychain.js';
7
- import { NewStorageQuery } from '../storage/index.js';
8
- import { BaseSubstrateClient, ensurePresence } from './BaseSubstrateClient.js';
1
+ import { isJsonRpcProvider } from '../json-rpc/index.js';
2
+ import { LegacyClient } from './LegacyClient.js';
3
+ import { V2Client } from './V2Client.js';
9
4
  /**
10
5
  * @name DedotClient
11
- * @description New promised-based API Client for Polkadot & Substrate based on JSON-RPC V2
6
+ * @description The main entry point for interacting with PolkadotSDK-based blockchains.
12
7
  *
13
- * __Unstable, use with caution.__
8
+ * DedotClient is a facade that provides a unified API for both JSON-RPC v2 (default) and legacy
9
+ * JSON-RPC versions.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { DedotClient, WsProvider } from 'dedot';
14
+ * import type { PolkadotApi } from '@dedot/chaintypes/polkadot';
15
+ *
16
+ * // Create and connect to a Polkadot node
17
+ * const provider = new WsProvider('wss://rpc.polkadot.io');
18
+ * const client = await DedotClient.new<PolkadotApi>(provider);
19
+ *
20
+ * // Get the current best block
21
+ * const bestBlock = await client.block.best();
22
+ * console.log('Best block:', bestBlock.number, bestBlock.hash);
23
+ *
24
+ * // Subscribe to finalized blocks
25
+ * const unsub = client.block.finalized((block) => {
26
+ * console.log('Finalized:', block.number);
27
+ * });
28
+ *
29
+ * // Query on-chain storage
30
+ * const balance = await client.query.system.account('14...');
31
+ * console.log('Balance:', balance);
32
+ *
33
+ * // Subscribe to runtime upgrades
34
+ * const unsub = client.on('runtimeUpgraded', (version, block) => {
35
+ * console.log('Runtime upgraded to:', version.specVersion, 'at block:', block.number);
36
+ * });
37
+ *
38
+ * // Sending transactions
39
+ * await client.tx.balances.transferKeepAlive('15...', 1000000000000n)
40
+ * .signAndSend(signer)
41
+ * .untilFinalized();
42
+ *
43
+ * // Disconnect when done
44
+ * await client.disconnect();
45
+ * ```
46
+ *
47
+ * @template ChainApi - Chain-specific API type for type-safe interactions (defaults to SubstrateApi)
14
48
  */
15
- export class DedotClient// prettier-end-here
16
- extends BaseSubstrateClient {
17
- _chainHead;
18
- _chainSpec;
19
- _archive;
20
- _txBroadcaster;
49
+ export class DedotClient {
50
+ #client;
51
+ /** The JSON-RPC version being used ('v2' or 'legacy') */
52
+ rpcVersion;
21
53
  /**
22
- * Use factory methods (`create`, `new`) to create `DedotClient` instances.
54
+ * Creates a new DedotClient instance.
55
+ *
56
+ * Use factory methods (`create`, `new`, `legacy`) for automatic connection.
23
57
  *
24
- * @param options
58
+ * @param options - Client configuration options or a JsonRpcProvider instance
25
59
  */
26
60
  constructor(options) {
27
- super('v2', options);
61
+ let rpcVersion = 'v2';
62
+ if (!isJsonRpcProvider(options)) {
63
+ if (options['rpcVersion'] === 'legacy') {
64
+ rpcVersion = 'legacy';
65
+ }
66
+ }
67
+ this.rpcVersion = rpcVersion;
68
+ if (this.rpcVersion === 'legacy') {
69
+ this.#client = new LegacyClient(options);
70
+ }
71
+ else {
72
+ this.#client = new V2Client(options);
73
+ }
28
74
  }
29
75
  /**
30
- * Factory method to create a new DedotClient instance
76
+ * Factory method to create and connect a new DedotClient instance.
31
77
  *
32
- * @param options
78
+ * @param options - Client configuration options or a JsonRpcProvider instance
79
+ * @returns A connected DedotClient instance
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const client = await DedotClient.create<PolkadotApi>({
84
+ * provider: new WsProvider('wss://rpc.polkadot.io'),
85
+ * });
86
+ * ```
33
87
  */
34
88
  static async create(options) {
35
89
  return new DedotClient(options).connect();
36
90
  }
37
91
  /**
38
- * Alias for __DedotClient.create__
92
+ * Alias for `DedotClient.create`
39
93
  *
40
- * @param options
94
+ * @param options - Client configuration options or a JsonRpcProvider instance
95
+ * @returns A connected DedotClient instance
41
96
  */
42
97
  static async new(options) {
43
98
  return DedotClient.create(options);
44
99
  }
45
- get chainSpec() {
46
- return ensurePresence(this._chainSpec);
100
+ /**
101
+ * Factory method to create a DedotClient using legacy JSON-RPC.
102
+ *
103
+ * This is a convenience method that automatically sets `rpcVersion: 'legacy'`.
104
+ *
105
+ * @param options - Client configuration options or a JsonRpcProvider instance
106
+ * @returns A connected DedotClient instance using legacy JSON-RPC
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const client = await DedotClient.legacy<PolkadotApi>({
111
+ * provider: new WsProvider('wss://rpc.polkadot.io'),
112
+ * });
113
+ * ```
114
+ */
115
+ static async legacy(options) {
116
+ return DedotClient.create(isJsonRpcProvider(options) // --
117
+ ? { provider: options, rpcVersion: 'legacy' }
118
+ : { ...options, rpcVersion: 'legacy' });
119
+ }
120
+ /** The API configuration options */
121
+ get options() {
122
+ return this.#client.options;
123
+ }
124
+ /** Current connection status */
125
+ get status() {
126
+ return this.#client.status;
127
+ }
128
+ /** The underlying JSON-RPC provider */
129
+ get provider() {
130
+ return this.#client.provider;
131
+ }
132
+ /** Transaction builder for submitting extrinsics */
133
+ get tx() {
134
+ return this.#client.tx;
135
+ }
136
+ /** Raw JSON-RPC method access */
137
+ get rpc() {
138
+ return this.#client.rpc;
139
+ }
140
+ /** The genesis hash of the connected chain */
141
+ get genesisHash() {
142
+ return this.#client.genesisHash;
47
143
  }
48
- get chainHead() {
49
- return ensurePresence(this._chainHead);
144
+ /** Current runtime version information */
145
+ get runtimeVersion() {
146
+ return this.#client.runtimeVersion;
50
147
  }
51
- async archive() {
52
- assert(this._archive, 'Archive instance is not initialized');
53
- assert(await this._archive.supported(), 'Archive JSON-RPC is not supported by the connected server');
54
- return this._archive;
148
+ /** The chain metadata */
149
+ get metadata() {
150
+ return this.#client.metadata;
55
151
  }
56
- get txBroadcaster() {
57
- this.chainHead; // Ensure chain head is initialized
58
- assert(this._txBroadcaster, 'JSON-RPC method to broadcast transactions is not supported by the server/node.');
59
- return this._txBroadcaster;
152
+ /** Type registry for encoding/decoding chain types */
153
+ get registry() {
154
+ return this.#client.registry;
60
155
  }
61
- async #initializeTxBroadcaster(rpcMethods) {
62
- const tx = new Transaction(this, { rpcMethods });
63
- if (await tx.supported())
64
- return tx;
65
- const txWatch = new TransactionWatch(this, { rpcMethods });
66
- if (await txWatch.supported())
67
- return txWatch;
156
+ /**
157
+ * Access to pallet constants.
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const existentialDeposit = client.consts.balances.existentialDeposit;
162
+ * const ss58Prefix = client.consts.system.ss58Prefix;
163
+ * ```
164
+ */
165
+ get consts() {
166
+ return this.#client.consts;
68
167
  }
69
168
  /**
70
- * Initialize APIs before usage
169
+ * Storage query interface for reading on-chain state.
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // One-time query
174
+ * const account = await client.query.system.account(address);
175
+ *
176
+ * // Subscribe to storage changes
177
+ * const unsub = await client.query.system.number((blockNumber) => {
178
+ * console.log('Current block:', blockNumber);
179
+ * });
180
+ * ```
71
181
  */
72
- async doInitialize() {
73
- const rpcMethods = (await this.rpc.rpc_methods()).methods;
74
- this._chainHead = new ChainHead(this, { rpcMethods });
75
- this._chainSpec = new ChainSpec(this, { rpcMethods });
76
- // Always initialize Archive, but only set up fallback if supported
77
- this._archive = new Archive(this, { rpcMethods });
78
- // Set up ChainHead with Archive fallback only if Archive is supported
79
- if (await this._archive.supported()) {
80
- this._chainHead.withArchive(this._archive);
81
- }
82
- this._txBroadcaster = await this.#initializeTxBroadcaster(rpcMethods);
83
- // Fetching node information
84
- let [_, genesisHash] = await Promise.all([
85
- this.chainHead.follow(),
86
- this.chainSpec.genesisHash().catch(() => undefined),
87
- ]);
88
- this._genesisHash = genesisHash || (await this.#getGenesisHashFallback());
89
- this._runtimeVersion = await this.chainHead.bestRuntimeVersion();
90
- let metadata;
91
- if (await this.shouldPreloadMetadata()) {
92
- metadata = await this.fetchMetadata();
93
- }
94
- await this.setupMetadata(metadata);
95
- this.subscribeRuntimeUpgrades();
96
- // relegate events
97
- this.chainHead.on('newBlock', (...args) => this.emit('newBlock', ...args));
98
- this.chainHead.on('bestBlock', (...args) => this.emit('bestBlock', ...args));
99
- this.chainHead.on('finalizedBlock', (...args) => this.emit('finalizedBlock', ...args));
100
- this.chainHead.on('bestChainChanged', (...args) => this.emit('bestChainChanged', ...args));
182
+ get query() {
183
+ return this.#client.query;
101
184
  }
102
185
  /**
103
- * Ref: https://github.com/paritytech/polkadot-sdk/blob/bbd51ce867967f71657b901f1a956ad4f75d352e/substrate/frame/system/src/lib.rs#L909-L913
104
- * @private
186
+ * Runtime API call interface.
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * const rawMetadata = await client.call.metadata.metadataAtVersion(16);
191
+ * const version = await client.call.core.version();
192
+ * ```
105
193
  */
106
- async #getGenesisHashFallback() {
107
- const pallet = xxhashAsU8a('System', 128);
108
- const item = xxhashAsU8a('BlockHash', 128);
109
- const blockHeightAt0 = twox64Concat(u32.encode(0));
110
- const key = u8aToHex(concatU8a(pallet, item, blockHeightAt0));
111
- const storageValue = await this.chainHead.storage([{ type: 'value', key }]);
112
- const rawGenesisHash = storageValue.at(0)?.value;
113
- assert(rawGenesisHash, 'Genesis hash not found!');
114
- // Here we assume that in most case the hash is stored as a H256
115
- return $H256.tryDecode(rawGenesisHash);
116
- }
117
- subscribeRuntimeUpgrades() {
118
- this.chainHead.on('bestBlock', this.onRuntimeUpgrade);
119
- }
120
- onRuntimeUpgrade = async (block) => {
121
- const runtimeUpgraded = block.runtime && block.runtime.specVersion !== this._runtimeVersion?.specVersion;
122
- if (!runtimeUpgraded)
123
- return;
124
- this.startRuntimeUpgrade();
125
- this._runtimeVersion = block.runtime;
126
- const newMetadata = await this.fetchMetadata(undefined, this._runtimeVersion);
127
- await this.setupMetadata(newMetadata);
128
- this.emit('runtimeUpgraded', this._runtimeVersion);
129
- this.doneRuntimeUpgrade();
130
- };
131
- async beforeDisconnect() {
132
- await this.chainHead.unfollow();
133
- }
134
- onDisconnected = async () => {
135
- try {
136
- this.chainHead.unfollow().catch(noop);
137
- }
138
- catch { }
139
- };
140
- cleanUp() {
141
- super.cleanUp();
142
- this._chainHead = undefined;
143
- this._chainSpec = undefined;
144
- this._archive = undefined;
145
- this._txBroadcaster = undefined;
194
+ get call() {
195
+ return this.#client.call;
146
196
  }
147
197
  /**
148
- * @description Clear local cache, API at-block cache, and ChainHead cache
149
- * @param keepMetadataCache Keep the metadata cache, only clear other caches.
198
+ * Event type definitions and utilities.
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * // Check if an event matches a specific type
203
+ * if (client.events.balances.Transfer.is(event)) {
204
+ * console.log('Transfer event:', event.data);
205
+ * }
206
+ * ```
150
207
  */
151
- async clearCache(keepMetadataCache = false) {
152
- await super.clearCache(keepMetadataCache);
153
- this._chainHead?.clearCache();
208
+ get events() {
209
+ return this.#client.events;
154
210
  }
155
- get query() {
156
- return newProxyChain({
157
- executor: new StorageQueryExecutorV2(this, this.chainHead),
158
- });
211
+ /**
212
+ * Error type definitions and utilities.
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * // Check if an error matches a specific type
217
+ * if (client.errors.balances.InsufficientBalance.is(dispatchError)) {
218
+ * console.log('Insufficient balance error');
219
+ * }
220
+ * ```
221
+ */
222
+ get errors() {
223
+ return this.#client.errors;
159
224
  }
225
+ /**
226
+ * View functions interface (requires Metadata V16+).
227
+ *
228
+ * @example
229
+ * ```typescript
230
+ * // Call a view function
231
+ * const result = await client.view.voterList.scores(ALICE_ADDRESS);
232
+ * ```
233
+ */
160
234
  get view() {
161
- return newProxyChain({
162
- executor: new ViewFunctionExecutorV2(this, this.chainHead),
163
- });
235
+ return this.#client.view;
164
236
  }
165
- get call() {
166
- return this.callAt();
237
+ /**
238
+ * Block explorer interface for accessing block data.
239
+ *
240
+ * Provides methods to get/subscribe to best and finalized blocks,
241
+ * as well as retrieve block headers and bodies.
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * // Get the current best block
246
+ * const bestBlock = await client.block.best();
247
+ * console.log('Best block:', bestBlock.number, bestBlock.hash);
248
+ *
249
+ * // Subscribe to finalized blocks
250
+ * const unsub = client.block.finalized((block) => {
251
+ * console.log('Finalized block:', block.number);
252
+ * });
253
+ *
254
+ * // Get block header and body
255
+ * const header = await client.block.header(blockHash);
256
+ * const body = await client.block.body(blockHash);
257
+ * ```
258
+ */
259
+ get block() {
260
+ return this.#client.block;
167
261
  }
168
- callAt(blockHash) {
169
- return newProxyChain({
170
- executor: new RuntimeApiExecutorV2(this, this.chainHead, blockHash),
171
- });
262
+ /**
263
+ * Chain specification interface for accessing chain information.
264
+ *
265
+ * Provides methods to get chain name, genesis hash, and chain properties.
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * const chainName = await client.chainSpec.chainName();
270
+ * const genesisHash = await client.chainSpec.genesisHash();
271
+ * const properties = await client.chainSpec.properties();
272
+ *
273
+ * console.log(`Connected to ${chainName}`);
274
+ * console.log('Token symbol:', properties.tokenSymbol);
275
+ * console.log('Token decimals:', properties.tokenDecimals);
276
+ * ```
277
+ */
278
+ get chainSpec() {
279
+ return this.#client.chainSpec;
172
280
  }
173
- get tx() {
174
- return newProxyChain({ executor: new TxExecutorV2(this) });
281
+ /**
282
+ * Establishes connection to the blockchain network.
283
+ *
284
+ * @returns This client instance for method chaining
285
+ */
286
+ async connect() {
287
+ await this.#client.connect();
288
+ return this;
175
289
  }
176
290
  /**
177
- * Get a new API instance at a specific block hash
178
- * Supports both pinned blocks (via ChainHead) and historical blocks (via Archive fallback)
291
+ * Closes the connection to the blockchain network.
292
+ */
293
+ async disconnect() {
294
+ await this.#client.disconnect();
295
+ }
296
+ /**
297
+ * Subscribe to client events.
179
298
  *
180
- * @param hash
299
+ * @param event - The event to listen for ('ready', 'connected', 'disconnected', 'reconnecting', 'runtimeUpgraded', 'error')
300
+ * @param handler - Callback function to handle the event
301
+ * @returns Unsubscribe function
181
302
  */
182
- async at(hash) {
183
- const cached = this._apiAtCache.get(hash);
184
- if (cached)
185
- return cached;
186
- let parentVersion;
187
- let parentHash;
188
- // Try to get block info from ChainHead first (for pinned blocks)
189
- const targetBlock = this.chainHead.findBlock(hash);
190
- if (targetBlock) {
191
- if (hash === this.genesisHash) {
192
- parentHash = hash;
193
- parentVersion = targetBlock.runtime;
194
- }
195
- else {
196
- parentHash = targetBlock.parent;
197
- const parentBlock = this.chainHead.findBlock(parentHash);
198
- parentVersion = parentBlock?.runtime;
199
- }
200
- // fallback to fetching on-chain runtime if we can't find it in the block
201
- if (!parentVersion) {
202
- parentVersion = this.toSubstrateRuntimeVersion(await this.callAt(parentHash).core.version());
203
- }
204
- }
205
- else {
206
- // Block not pinned, try via Archive fallback if supported
207
- if (this._archive && (await this._archive.supported())) {
208
- try {
209
- if (hash === this.genesisHash) {
210
- parentHash = hash;
211
- }
212
- else {
213
- const rawHeader = await this._archive.header(hash);
214
- assert(rawHeader, `Header for block ${hash} not found`);
215
- const header = $Header.tryDecode(rawHeader);
216
- parentHash = header.parentHash;
217
- }
218
- // Fetch runtime version via Archive
219
- const runtimeRaw = await this._archive.call('Core_version', '0x', parentHash);
220
- assert(runtimeRaw, 'Runtime Version Not Found');
221
- parentVersion = this.toSubstrateRuntimeVersion($RuntimeVersion.tryDecode(runtimeRaw));
222
- }
223
- catch (error) {
224
- throw new DedotError(`Unable to fetch runtime version for block ${hash}: ${error}`);
225
- }
226
- }
227
- else {
228
- throw new DedotError('Block is not pinned and Archive JSON-RPC is not supported by the server/node!');
229
- }
230
- }
231
- let metadata = this.metadata;
232
- let registry = this.registry;
233
- if (parentVersion && parentVersion.specVersion !== this.runtimeVersion.specVersion) {
234
- const cachedMetadata = this.findMetadataInCache(parentVersion.specVersion);
235
- if (cachedMetadata) {
236
- metadata = cachedMetadata[0];
237
- registry = cachedMetadata[1];
238
- }
239
- else {
240
- metadata = await this.fetchMetadata(parentHash, parentVersion);
241
- registry = new PortableRegistry(metadata.latest, this.options.hasher);
242
- }
243
- }
244
- const api = {
245
- rpcVersion: 'v2',
246
- atBlockHash: hash,
247
- options: this.options,
248
- genesisHash: this.genesisHash,
249
- runtimeVersion: parentVersion,
250
- metadata,
251
- registry,
252
- rpc: this.rpc,
253
- };
254
- api.consts = newProxyChain({ executor: new ConstantExecutor(api) });
255
- api.events = newProxyChain({ executor: new EventExecutor(api) });
256
- api.errors = newProxyChain({ executor: new ErrorExecutor(api) });
257
- api.query = newProxyChain({ executor: new StorageQueryExecutorV2(api, this.chainHead) });
258
- api.call = newProxyChain({ executor: new RuntimeApiExecutorV2(api, this.chainHead) });
259
- api.view = newProxyChain({ executor: new ViewFunctionExecutorV2(api, this.chainHead) });
260
- // @ts-ignore Add queryMulti implementation for at-block queries
261
- api.queryMulti = (queries) => {
262
- return this.internalQueryMulti(queries, undefined, hash);
263
- };
264
- this._apiAtCache.set(hash, api);
265
- return api;
266
- }
267
- getStorageQuery() {
268
- return new NewStorageQuery(this);
303
+ on(event, handler) {
304
+ return this.#client.on(event, handler);
305
+ }
306
+ /**
307
+ * Subscribe to a client event once.
308
+ *
309
+ * @param event - The event to listen for
310
+ * @param handler - Callback function to handle the event
311
+ * @returns Unsubscribe function
312
+ */
313
+ once(event, handler) {
314
+ return this.#client.once(event, handler);
315
+ }
316
+ /**
317
+ * Unsubscribe from client events.
318
+ *
319
+ * @param event - The event to unsubscribe from
320
+ * @param handler - The handler function to remove (optional, removes all handlers if not provided)
321
+ * @returns This client instance for method chaining
322
+ */
323
+ off(event, handler) {
324
+ this.#client.off(event, handler);
325
+ return this;
326
+ }
327
+ /**
328
+ * Get a client instance at a specific block hash.
329
+ *
330
+ * This allows querying historical state at a specific block.
331
+ *
332
+ * @template ChainApiAt - Chain API type for the historical state (defaults to ChainApi)
333
+ * @param hash - The block hash to query at
334
+ * @returns A client instance for querying state at the specified block
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * const clientAtBlock = await client.at('0x1234...');
339
+ * const historicalBalance = await clientAtBlock.query.system.account('14...');
340
+ * ```
341
+ */
342
+ at(hash) {
343
+ return this.#client.at(hash);
344
+ }
345
+ /**
346
+ * Get the current runtime version with metadata sync.
347
+ *
348
+ * Unlike the `runtimeVersion` getter, this method ensures the corresponding
349
+ * metadata for the runtime version is downloaded and set up. Useful for
350
+ * preparing for runtime upgrades.
351
+ *
352
+ * @returns The current runtime version
353
+ */
354
+ getRuntimeVersion() {
355
+ return this.#client.getRuntimeVersion();
356
+ }
357
+ /**
358
+ * Set or update the signer instance for signing transactions.
359
+ *
360
+ * @param signer - The signer instance (or undefined to clear)
361
+ */
362
+ setSigner(signer) {
363
+ this.#client.setSigner(signer);
364
+ }
365
+ queryMulti(queries, // --
366
+ callback) {
367
+ // @ts-ignore
368
+ return this.#client.queryMulti(queries, callback);
369
+ }
370
+ /**
371
+ * Broadcast a transaction to the network and track its status.
372
+ *
373
+ * @param tx - The transaction (hex string or Extrinsic instance)
374
+ * @param callback - Optional callback for transaction status updates
375
+ * @returns TxUnsub object with utility methods (`.untilFinalized()`, `.untilBestChainBlockIncluded()`)
376
+ *
377
+ * @example
378
+ * ```typescript
379
+ * // Wait for finalization
380
+ * const result = await client.sendTx(txHex).untilFinalized();
381
+ *
382
+ * // With status callback to track progress
383
+ * const unsub = await client.sendTx(txHex, (result) => {
384
+ * console.log('Status:', result.status);
385
+ * if (result.dispatchError) {
386
+ * console.error('Transaction failed:', result.dispatchError);
387
+ * }
388
+ * });
389
+ * ```
390
+ */
391
+ sendTx(tx, callback) {
392
+ return this.#client.sendTx(tx, callback);
393
+ }
394
+ /**
395
+ * Clear internal caches.
396
+ *
397
+ * @param keepMetadataCache - If true, preserves the metadata cache (default: false)
398
+ */
399
+ async clearCache(keepMetadataCache = false) {
400
+ await this.#client.clearCache(keepMetadataCache);
269
401
  }
270
402
  }