@dedot/api 0.15.2 → 0.16.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 (49) hide show
  1. package/chaintypes/substrate/index.d.ts +21 -1
  2. package/cjs/client/BaseSubstrateClient.js +12 -3
  3. package/cjs/client/DedotClient.js +51 -11
  4. package/cjs/client/LegacyClient.js +4 -5
  5. package/cjs/executor/Executor.js +1 -0
  6. package/cjs/executor/v2/StorageQueryExecutorV2.js +1 -3
  7. package/cjs/executor/v2/TxExecutorV2.js +5 -3
  8. package/cjs/json-rpc/group/Archive.js +232 -0
  9. package/cjs/json-rpc/group/ChainHead/ChainHead.js +149 -52
  10. package/cjs/json-rpc/group/ChainHead/error.js +5 -0
  11. package/cjs/json-rpc/group/index.js +1 -0
  12. package/cjs/json-rpc/subscriptionsInfo.js +4 -0
  13. package/cjs/storage/LegacyStorageQuery.js +5 -0
  14. package/cjs/storage/NewStorageQuery.js +5 -0
  15. package/client/BaseSubstrateClient.d.ts +8 -6
  16. package/client/BaseSubstrateClient.js +13 -4
  17. package/client/DedotClient.d.ts +11 -4
  18. package/client/DedotClient.js +54 -14
  19. package/client/LegacyClient.d.ts +2 -2
  20. package/client/LegacyClient.js +4 -5
  21. package/executor/Executor.d.ts +5 -5
  22. package/executor/Executor.js +1 -0
  23. package/executor/StorageQueryExecutor.d.ts +2 -2
  24. package/executor/v2/RuntimeApiExecutorV2.d.ts +2 -2
  25. package/executor/v2/StorageQueryExecutorV2.d.ts +4 -4
  26. package/executor/v2/StorageQueryExecutorV2.js +1 -3
  27. package/executor/v2/TxExecutorV2.d.ts +2 -1
  28. package/executor/v2/TxExecutorV2.js +5 -3
  29. package/executor/v2/ViewFunctionExecutorV2.d.ts +2 -2
  30. package/extrinsic/extensions/SignedExtension.d.ts +3 -3
  31. package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +2 -2
  32. package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +2 -2
  33. package/json-rpc/group/Archive.d.ts +134 -0
  34. package/json-rpc/group/Archive.js +228 -0
  35. package/json-rpc/group/ChainHead/ChainHead.d.ts +21 -0
  36. package/json-rpc/group/ChainHead/ChainHead.js +150 -53
  37. package/json-rpc/group/ChainHead/error.d.ts +3 -0
  38. package/json-rpc/group/ChainHead/error.js +5 -0
  39. package/json-rpc/group/index.d.ts +1 -0
  40. package/json-rpc/group/index.js +1 -0
  41. package/json-rpc/subscriptionsInfo.js +4 -0
  42. package/package.json +9 -9
  43. package/storage/BaseStorageQuery.d.ts +5 -6
  44. package/storage/LegacyStorageQuery.d.ts +4 -3
  45. package/storage/LegacyStorageQuery.js +5 -0
  46. package/storage/NewStorageQuery.d.ts +4 -3
  47. package/storage/NewStorageQuery.js +5 -0
  48. package/storage/QueryableStorage.d.ts +2 -2
  49. package/types.d.ts +11 -6
@@ -1,4 +1,5 @@
1
- import type { GenericSubstrateApi, RpcLegacy, RpcV2, RpcVersion } from '@dedot/types';
1
+ import type { MultiAddress } from '@dedot/codecs';
2
+ import type { GenericChainKnownTypes, GenericSubstrateApi, RpcLegacy, RpcV2, RpcVersion } from '@dedot/types';
2
3
  import { ChainConsts } from './consts.js';
3
4
  import { ChainErrors } from './errors.js';
4
5
  import { ChainEvents } from './events.js';
@@ -6,8 +7,26 @@ import { ChainJsonRpcApis } from './json-rpc.js';
6
7
  import { ChainStorage } from './query.js';
7
8
  import { RuntimeApis } from './runtime.js';
8
9
  import { ChainTx } from './tx.js';
10
+ import type { FrameMetadataHashExtensionCheckMetadataHash, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckMortality, FrameSystemExtensionsCheckNonZeroSender, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemExtensionsWeightReclaim, KitchensinkRuntimeRuntimeCall, PalletAssetConversionTxPaymentChargeAssetTxPayment, SpRuntimeMultiSignature } from './types.js';
9
11
  import { ChainViewFunctions } from './view-functions.js';
10
12
  export * from './types.js';
13
+ interface ChainKnownTypes extends GenericChainKnownTypes {
14
+ Address: MultiAddress;
15
+ Signature: SpRuntimeMultiSignature;
16
+ RuntimeCall: KitchensinkRuntimeRuntimeCall;
17
+ Extra: [
18
+ FrameSystemExtensionsCheckNonZeroSender,
19
+ FrameSystemExtensionsCheckSpecVersion,
20
+ FrameSystemExtensionsCheckTxVersion,
21
+ FrameSystemExtensionsCheckGenesis,
22
+ FrameSystemExtensionsCheckMortality,
23
+ FrameSystemExtensionsCheckNonce,
24
+ FrameSystemExtensionsCheckWeight,
25
+ PalletAssetConversionTxPaymentChargeAssetTxPayment,
26
+ FrameMetadataHashExtensionCheckMetadataHash,
27
+ FrameSystemExtensionsWeightReclaim
28
+ ];
29
+ }
11
30
  export interface VersionedSubstrateApi<Rv extends RpcVersion> extends GenericSubstrateApi<Rv> {
12
31
  rpc: ChainJsonRpcApis<Rv>;
13
32
  consts: ChainConsts<Rv>;
@@ -17,6 +36,7 @@ export interface VersionedSubstrateApi<Rv extends RpcVersion> extends GenericSub
17
36
  call: RuntimeApis<Rv>;
18
37
  view: ChainViewFunctions<Rv>;
19
38
  tx: ChainTx<Rv>;
39
+ types: ChainKnownTypes;
20
40
  }
21
41
  /**
22
42
  * @name: SubstrateApi
@@ -10,6 +10,8 @@ const proxychain_js_1 = require("../proxychain.js");
10
10
  const index_js_3 = require("../storage/index.js");
11
11
  const SUPPORTED_METADATA_VERSIONS = [16, 15, 14];
12
12
  const MetadataApiHash = (0, utils_1.calcRuntimeApiHash)('Metadata'); // 0x37e397fc7c91f5e4
13
+ const API_AT_CACHE_CAPACITY = 64;
14
+ const API_AT_CACHE_TTL = 300_000; // 5 minutes
13
15
  const MESSAGE = 'Make sure to call `.connect()` method first before using the API interfaces.';
14
16
  function ensurePresence(value) {
15
17
  return (0, utils_1.ensurePresence)(value, MESSAGE);
@@ -28,10 +30,12 @@ class BaseSubstrateClient extends index_js_2.JsonRpcClient {
28
30
  _runtimeVersion;
29
31
  _localCache;
30
32
  _runtimeUpgrading;
33
+ _apiAtCache;
31
34
  constructor(rpcVersion, options) {
32
35
  super(options);
33
36
  this.rpcVersion = rpcVersion;
34
37
  this._options = this.normalizeOptions(options);
38
+ this._apiAtCache = new utils_1.LRUCache(API_AT_CACHE_CAPACITY, API_AT_CACHE_TTL);
35
39
  }
36
40
  /// --- Internal logics
37
41
  normalizeOptions(options) {
@@ -189,12 +193,17 @@ class BaseSubstrateClient extends index_js_2.JsonRpcClient {
189
193
  this._genesisHash = undefined;
190
194
  this._runtimeVersion = undefined;
191
195
  this._localCache = undefined;
196
+ this._apiAtCache.clear();
192
197
  }
193
198
  /**
194
- * @description Clear local cache
199
+ * @description Clear local cache and API at-block cache
200
+ * @param keepMetadataCache Keep the metadata cache, only clear other caches.
195
201
  */
196
- async clearCache() {
197
- await this._localCache?.clear();
202
+ async clearCache(keepMetadataCache = false) {
203
+ if (!keepMetadataCache) {
204
+ await this._localCache?.clear();
205
+ }
206
+ this._apiAtCache.clear();
198
207
  }
199
208
  async doConnect() {
200
209
  // @ts-ignore
@@ -19,8 +19,8 @@ class DedotClient// prettier-end-here
19
19
  extends BaseSubstrateClient_js_1.BaseSubstrateClient {
20
20
  _chainHead;
21
21
  _chainSpec;
22
+ _archive;
22
23
  _txBroadcaster;
23
- #apiAtCache = {};
24
24
  /**
25
25
  * Use factory methods (`create`, `new`) to create `DedotClient` instances.
26
26
  *
@@ -51,6 +51,11 @@ class DedotClient// prettier-end-here
51
51
  get chainHead() {
52
52
  return (0, BaseSubstrateClient_js_1.ensurePresence)(this._chainHead);
53
53
  }
54
+ async archive() {
55
+ (0, utils_1.assert)(this._archive, 'Archive instance is not initialized');
56
+ (0, utils_1.assert)(await this._archive.supported(), 'Archive JSON-RPC is not supported by the connected server');
57
+ return this._archive;
58
+ }
54
59
  get txBroadcaster() {
55
60
  this.chainHead; // Ensure chain head is initialized
56
61
  (0, utils_1.assert)(this._txBroadcaster, 'JSON-RPC method to broadcast transactions is not supported by the server/node.');
@@ -71,6 +76,12 @@ class DedotClient// prettier-end-here
71
76
  const rpcMethods = (await this.rpc.rpc_methods()).methods;
72
77
  this._chainHead = new index_js_2.ChainHead(this, { rpcMethods });
73
78
  this._chainSpec = new index_js_2.ChainSpec(this, { rpcMethods });
79
+ // Always initialize Archive, but only set up fallback if supported
80
+ this._archive = new index_js_2.Archive(this, { rpcMethods });
81
+ // Set up ChainHead with Archive fallback only if Archive is supported
82
+ if (await this._archive.supported()) {
83
+ this._chainHead.withArchive(this._archive);
84
+ }
74
85
  this._txBroadcaster = await this.#initializeTxBroadcaster(rpcMethods);
75
86
  // Fetching node information
76
87
  let [_, genesisHash] = await Promise.all([
@@ -133,8 +144,16 @@ class DedotClient// prettier-end-here
133
144
  super.cleanUp();
134
145
  this._chainHead = undefined;
135
146
  this._chainSpec = undefined;
147
+ this._archive = undefined;
136
148
  this._txBroadcaster = undefined;
137
- this.#apiAtCache = {};
149
+ }
150
+ /**
151
+ * @description Clear local cache, API at-block cache, and ChainHead cache
152
+ * @param keepMetadataCache Keep the metadata cache, only clear other caches.
153
+ */
154
+ async clearCache(keepMetadataCache = false) {
155
+ await super.clearCache(keepMetadataCache);
156
+ this._chainHead?.clearCache();
138
157
  }
139
158
  get query() {
140
159
  return (0, proxychain_js_1.newProxyChain)({
@@ -159,19 +178,40 @@ class DedotClient// prettier-end-here
159
178
  }
160
179
  /**
161
180
  * Get a new API instance at a specific block hash
162
- * For now, this only supports pinned block hashes from the chain head
181
+ * Supports both pinned blocks (via ChainHead) and historical blocks (via Archive fallback)
163
182
  *
164
183
  * @param hash
165
184
  */
166
185
  async at(hash) {
167
- if (this.#apiAtCache[hash])
168
- return this.#apiAtCache[hash];
186
+ const cached = this._apiAtCache.get(hash);
187
+ if (cached)
188
+ return cached;
189
+ let targetVersion;
190
+ // Try to get block info from ChainHead first (for pinned blocks)
169
191
  const targetBlock = this.chainHead.findBlock(hash);
170
- (0, utils_1.assert)(targetBlock, 'Block is not pinned!');
171
- let targetVersion = targetBlock.runtime;
172
- if (!targetVersion) {
173
- // fallback to fetching on-chain runtime if we can't find it in the block
174
- targetVersion = this.toSubstrateRuntimeVersion(await this.callAt(hash).core.version());
192
+ if (targetBlock) {
193
+ targetVersion = targetBlock.runtime;
194
+ if (!targetVersion) {
195
+ // fallback to fetching on-chain runtime if we can't find it in the block
196
+ targetVersion = this.toSubstrateRuntimeVersion(await this.callAt(hash).core.version());
197
+ }
198
+ }
199
+ else {
200
+ // Block not pinned, try via Archive fallback if supported
201
+ if (this._archive && (await this._archive.supported())) {
202
+ try {
203
+ // Fetch runtime version via Archive
204
+ const runtimeRaw = await this._archive.call('Core_version', '0x', hash);
205
+ (0, utils_1.assert)(runtimeRaw, 'Runtime Version Not Found');
206
+ targetVersion = this.toSubstrateRuntimeVersion(codecs_1.$RuntimeVersion.tryDecode(runtimeRaw));
207
+ }
208
+ catch (error) {
209
+ throw new utils_1.DedotError(`Unable to fetch runtime version for block ${hash}: ${error}`);
210
+ }
211
+ }
212
+ else {
213
+ throw new utils_1.DedotError('Block is not pinned and Archive JSON-RPC is not supported by the server/node!');
214
+ }
175
215
  }
176
216
  let metadata = this.metadata;
177
217
  let registry = this.registry;
@@ -195,7 +235,7 @@ class DedotClient// prettier-end-here
195
235
  api.query = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.StorageQueryExecutorV2(api, this.chainHead) });
196
236
  api.call = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.RuntimeApiExecutorV2(api, this.chainHead) });
197
237
  api.view = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.ViewFunctionExecutorV2(api, this.chainHead) });
198
- this.#apiAtCache[hash] = api;
238
+ this._apiAtCache.set(hash, api);
199
239
  return api;
200
240
  }
201
241
  getStorageQuery() {
@@ -49,7 +49,6 @@ class LegacyClient// prettier-end-here
49
49
  extends BaseSubstrateClient_js_1.BaseSubstrateClient {
50
50
  #runtimeSubscriptionUnsub;
51
51
  #healthTimer;
52
- #apiAtCache = {};
53
52
  /**
54
53
  * Use factory methods (`create`, `new`) to create `Dedot` instances.
55
54
  *
@@ -96,7 +95,6 @@ class LegacyClient// prettier-end-here
96
95
  }
97
96
  cleanUp() {
98
97
  super.cleanUp();
99
- this.#apiAtCache = {};
100
98
  this.#healthTimer = undefined;
101
99
  this.#runtimeSubscriptionUnsub = undefined;
102
100
  }
@@ -232,8 +230,9 @@ class LegacyClient// prettier-end-here
232
230
  * @param hash
233
231
  */
234
232
  async at(hash) {
235
- if (this.#apiAtCache[hash])
236
- return this.#apiAtCache[hash];
233
+ const cached = this._apiAtCache.get(hash);
234
+ if (cached)
235
+ return cached;
237
236
  const targetVersion = await this.#getRuntimeVersion(hash);
238
237
  let metadata = this.metadata;
239
238
  let registry = this.registry;
@@ -256,7 +255,7 @@ class LegacyClient// prettier-end-here
256
255
  api.call = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.RuntimeApiExecutor(api) });
257
256
  api.events = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.EventExecutor(api) });
258
257
  api.errors = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.ErrorExecutor(api) });
259
- this.#apiAtCache[hash] = api;
258
+ this._apiAtCache.set(hash, api);
260
259
  return api;
261
260
  }
262
261
  getStorageQuery() {
@@ -14,6 +14,7 @@ class Executor {
14
14
  this.#atBlockHash = atBlockHash;
15
15
  }
16
16
  get atBlockHash() {
17
+ // @ts-ignore
17
18
  return this.#atBlockHash || this.client.atBlockHash;
18
19
  }
19
20
  get registry() {
@@ -22,9 +22,7 @@ class StorageQueryExecutorV2 extends StorageQueryExecutor_js_1.StorageQueryExecu
22
22
  const entries = async (...args) => {
23
23
  const withArgs = !!args && args.length > 0;
24
24
  const key = withArgs ? entry.encodeKey(args, true) : entry.prefixKey;
25
- const results = await this.chainHead.storage([
26
- { type: 'descendantsValues', key },
27
- ]);
25
+ const results = await this.chainHead.storage([{ type: 'descendantsValues', key }]);
28
26
  return results.map(({ key, value }) => [
29
27
  entry.decodeKey(key),
30
28
  entry.decodeValue(value),
@@ -9,9 +9,11 @@ const TxExecutor_js_1 = require("../TxExecutor.js");
9
9
  * @description Execute a transaction instruction, returns a submittable extrinsic
10
10
  */
11
11
  class TxExecutorV2 extends TxExecutor_js_1.TxExecutor {
12
- constructor(api) {
13
- (0, utils_1.assert)(api.rpcVersion === 'v2', 'Only supports JSON-RPC v2');
14
- super(api);
12
+ client;
13
+ constructor(client) {
14
+ (0, utils_1.assert)(client.rpcVersion === 'v2', 'Only supports JSON-RPC v2');
15
+ super(client);
16
+ this.client = client;
15
17
  }
16
18
  createExtrinsic(call) {
17
19
  return new index_js_1.SubmittableExtrinsicV2(this.client, call);
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Archive = void 0;
4
+ const utils_1 = require("@dedot/utils");
5
+ const JsonRpcGroup_js_1 = require("./JsonRpcGroup.js");
6
+ const ARCHIVE_CACHE_CAPACITY = 256;
7
+ const ARCHIVE_CACHE_TTL = 60_000; // 1 minutes - archive data is immutable
8
+ /**
9
+ * @name Archive
10
+ * Archive JSON-RPC methods for accessing historical blockchain data.
11
+ * Functions with the `archive` prefix allow obtaining the state of the chain
12
+ * at any point in the present or in the past.
13
+ *
14
+ * JSON-RPC V2: https://paritytech.github.io/json-rpc-interface-spec/api/archive.html
15
+ */
16
+ class Archive extends JsonRpcGroup_js_1.JsonRpcGroup {
17
+ #genesisHash;
18
+ #cache;
19
+ constructor(client, options) {
20
+ super(client, { prefix: 'archive', supportedVersions: ['unstable', 'v1'], ...options });
21
+ this.#cache = new utils_1.LRUCache(ARCHIVE_CACHE_CAPACITY, ARCHIVE_CACHE_TTL);
22
+ }
23
+ /**
24
+ * Retrieves the body (list of transactions) of a given block.
25
+ * Returns an array of strings containing the hexadecimal-encoded SCALE-codec-encoded
26
+ * transactions in that block. If no block with that hash is found, null.
27
+ *
28
+ * @param hash - The block hash (optional, defaults to current finalized block)
29
+ * @returns Array of transaction hashes or null if block not found
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // Get transactions from current finalized block
34
+ * const transactions = await archive.body();
35
+ *
36
+ * // Get transactions from specific block
37
+ * const transactions = await archive.body('0x1234...');
38
+ * ```
39
+ */
40
+ async body(hash) {
41
+ const blockHash = hash || (await this.finalizedHash());
42
+ const cacheKey = `${blockHash}::body`;
43
+ const cached = this.#cache.get(cacheKey);
44
+ if (cached !== null) {
45
+ return cached;
46
+ }
47
+ const result = await this.send('body', blockHash);
48
+ this.#cache.set(cacheKey, result);
49
+ return result;
50
+ }
51
+ /**
52
+ * Get the chain's genesis hash.
53
+ * Returns a string containing the hexadecimal-encoded hash of the genesis block of the chain.
54
+ * This value is cached after the first call.
55
+ *
56
+ * @returns The genesis block hash
57
+ */
58
+ async genesisHash() {
59
+ if (!this.#genesisHash) {
60
+ this.#genesisHash = await this.send('genesisHash');
61
+ }
62
+ return this.#genesisHash;
63
+ }
64
+ /**
65
+ * Get the block's header.
66
+ * Returns a string containing the hexadecimal-encoded SCALE-codec encoding header of the block.
67
+ *
68
+ * @param hash - The block hash (optional, defaults to current finalized block)
69
+ * @returns The encoded block header or null if block not found
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Get header of current finalized block
74
+ * const header = await archive.header();
75
+ *
76
+ * // Get header of specific block
77
+ * const header = await archive.header('0x1234...');
78
+ * ```
79
+ */
80
+ async header(hash) {
81
+ const blockHash = hash || (await this.finalizedHash());
82
+ const cacheKey = `${blockHash}::header`;
83
+ const cached = this.#cache.get(cacheKey);
84
+ if (cached !== null) {
85
+ return cached;
86
+ }
87
+ const result = await this.send('header', blockHash);
88
+ this.#cache.set(cacheKey, result);
89
+ return result;
90
+ }
91
+ /**
92
+ * Get the height of the current finalized block.
93
+ * Returns an integer height of the current finalized block of the chain.
94
+ *
95
+ * @returns The height of the finalized block
96
+ */
97
+ async finalizedHeight() {
98
+ return this.send('finalizedHeight');
99
+ }
100
+ /**
101
+ * Get the hash of the current finalized block.
102
+ * Returns a string containing the hexadecimal-encoded hash of the current finalized block.
103
+ * This is a convenience method that combines finalizedHeight() and hashByHeight().
104
+ *
105
+ * @returns The hash of the current finalized block
106
+ */
107
+ async finalizedHash() {
108
+ const height = await this.finalizedHeight();
109
+ const hashes = await this.hashByHeight(height);
110
+ if (hashes.length === 0) {
111
+ throw new Error(`No block found at finalized height ${height}`);
112
+ }
113
+ return hashes[0];
114
+ }
115
+ /**
116
+ * Get the hashes of blocks from the given height.
117
+ * Returns an array (possibly empty) of strings containing hexadecimal-encoded hashes of block headers.
118
+ *
119
+ * Note: For heights <= finalized height, there is guaranteed to be one block.
120
+ * For heights > finalized height, there may be zero, one or multiple blocks depending on forks.
121
+ *
122
+ * @param height - The block height
123
+ * @returns Array of block hashes at the given height
124
+ */
125
+ async hashByHeight(height) {
126
+ return this.send('hashByHeight', height);
127
+ }
128
+ /**
129
+ * Call into the Runtime API at a specified block's state.
130
+ *
131
+ * @param func - The runtime API function to call
132
+ * @param params - The parameters for the function call (SCALE-encoded)
133
+ * @param hash - The block hash (optional, defaults to current finalized block)
134
+ * @returns The result of the runtime call
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * // Call Core_version on current finalized block
139
+ * const version = await archive.call('Core_version', '0x');
140
+ *
141
+ * // Call Core_version on specific block
142
+ * const version = await archive.call('Core_version', '0x', '0x1234...');
143
+ * ```
144
+ */
145
+ async call(func, params, hash) {
146
+ const blockHash = hash || (await this.finalizedHash());
147
+ const cacheKey = `${blockHash}::call::${func}::${params}`;
148
+ const cached = this.#cache.get(cacheKey);
149
+ if (cached !== null) {
150
+ return cached;
151
+ }
152
+ const result = await this.send('call', blockHash, func, params);
153
+ if (!result.success) {
154
+ throw new utils_1.DedotError(result.error);
155
+ }
156
+ this.#cache.set(cacheKey, result.value);
157
+ return result.value;
158
+ }
159
+ /**
160
+ * Returns storage entries at a specific block's state via subscription.
161
+ *
162
+ * @param items - Array of storage queries with optional pagination
163
+ * @param childTrie - Optional child trie key
164
+ * @param callback - Callback to receive storage events
165
+ * @param hash - The block hash (optional, defaults to current finalized block)
166
+ * @returns Unsubscribe function
167
+ */
168
+ async #storageSubscription(items, childTrie, callback, hash) {
169
+ const blockHash = hash || (await this.finalizedHash());
170
+ return this.send('storage', blockHash, items, childTrie, callback);
171
+ }
172
+ /**
173
+ * Returns storage entries at a specific block's state.
174
+ * This method collects all storage events and returns them as a single result.
175
+ *
176
+ * @param items - Array of storage queries with optional pagination
177
+ * @param childTrie - Optional child trie key
178
+ * @param hash - The block hash (optional, defaults to current finalized block)
179
+ * @returns Storage results array
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // Query storage from current finalized block
184
+ * const results = await archive.storage([{ key: '0x1234', type: 'value' }]);
185
+ *
186
+ * // Query storage from specific block
187
+ * const results = await archive.storage([{ key: '0x1234', type: 'value' }], null, '0xabcd...');
188
+ * ```
189
+ */
190
+ async storage(items, childTrie, hash) {
191
+ return new Promise(async (resolve, reject) => {
192
+ const results = [];
193
+ const blockHash = hash || (await this.finalizedHash());
194
+ // Generate cache key
195
+ const cacheKey = `${blockHash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
196
+ // Check cache
197
+ const cached = this.#cache.get(cacheKey);
198
+ if (cached !== null) {
199
+ return resolve(cached);
200
+ }
201
+ this.#storageSubscription(items, childTrie || null, (event) => {
202
+ switch (event.event) {
203
+ case 'storage':
204
+ results.push(event);
205
+ break;
206
+ case 'storageDone':
207
+ // Set cache after successful completion
208
+ this.#cache.set(cacheKey, results);
209
+ resolve(results);
210
+ break;
211
+ case 'storageError':
212
+ reject(new utils_1.DedotError(event.error));
213
+ break;
214
+ }
215
+ }, blockHash).catch(reject);
216
+ });
217
+ }
218
+ /**
219
+ * Clears the internal cache used for storing archive query results.
220
+ * This can be useful for memory management or when you want to force fresh data retrieval.
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * // Clear all cached results
225
+ * archive.clearCache();
226
+ * ```
227
+ */
228
+ clearCache() {
229
+ this.#cache.clear();
230
+ }
231
+ }
232
+ exports.Archive = Archive;