@dedot/api 0.15.3-next.65898ecf.10 → 0.16.1-next.dd70d277.9
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.js +0 -1
- package/chaintypes/substrate/errors.js +0 -1
- package/chaintypes/substrate/events.js +0 -1
- package/chaintypes/substrate/json-rpc.js +0 -1
- package/chaintypes/substrate/query.js +0 -1
- package/chaintypes/substrate/runtime.js +0 -1
- package/chaintypes/substrate/tx.js +0 -1
- package/chaintypes/substrate/types.js +0 -1
- package/chaintypes/substrate/view-functions.js +0 -1
- package/cjs/chaintypes/substrate/consts.js +0 -1
- package/cjs/chaintypes/substrate/errors.js +0 -1
- package/cjs/chaintypes/substrate/events.js +0 -1
- package/cjs/chaintypes/substrate/json-rpc.js +0 -1
- package/cjs/chaintypes/substrate/query.js +0 -1
- package/cjs/chaintypes/substrate/runtime.js +0 -1
- package/cjs/chaintypes/substrate/tx.js +0 -1
- package/cjs/chaintypes/substrate/types.js +0 -1
- package/cjs/chaintypes/substrate/view-functions.js +0 -1
- package/cjs/client/BaseSubstrateClient.js +12 -3
- package/cjs/client/DedotClient.js +13 -6
- package/cjs/client/LegacyClient.js +4 -5
- package/cjs/extrinsic/extensions/known/index.js +0 -11
- package/cjs/json-rpc/group/Archive.js +15 -9
- package/cjs/json-rpc/group/ChainHead/ChainHead.js +34 -13
- package/client/BaseSubstrateClient.d.ts +7 -5
- package/client/BaseSubstrateClient.js +13 -4
- package/client/DedotClient.d.ts +5 -0
- package/client/DedotClient.js +13 -6
- package/client/LegacyClient.js +4 -5
- package/executor/Executor.d.ts +1 -1
- package/extrinsic/extensions/known/index.js +0 -11
- package/json-rpc/group/Archive.js +16 -10
- package/json-rpc/group/ChainHead/ChainHead.d.ts +11 -0
- package/json-rpc/group/ChainHead/ChainHead.js +35 -14
- package/package.json +9 -9
- package/storage/BaseStorageQuery.d.ts +1 -1
- package/storage/NewStorageQuery.d.ts +1 -1
- package/types.d.ts +2 -2
|
@@ -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
|
-
|
|
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
|
|
@@ -21,7 +21,6 @@ class DedotClient// prettier-end-here
|
|
|
21
21
|
_chainSpec;
|
|
22
22
|
_archive;
|
|
23
23
|
_txBroadcaster;
|
|
24
|
-
#apiAtCache = {};
|
|
25
24
|
/**
|
|
26
25
|
* Use factory methods (`create`, `new`) to create `DedotClient` instances.
|
|
27
26
|
*
|
|
@@ -147,7 +146,14 @@ class DedotClient// prettier-end-here
|
|
|
147
146
|
this._chainSpec = undefined;
|
|
148
147
|
this._archive = undefined;
|
|
149
148
|
this._txBroadcaster = undefined;
|
|
150
|
-
|
|
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();
|
|
151
157
|
}
|
|
152
158
|
get query() {
|
|
153
159
|
return (0, proxychain_js_1.newProxyChain)({
|
|
@@ -177,8 +183,9 @@ class DedotClient// prettier-end-here
|
|
|
177
183
|
* @param hash
|
|
178
184
|
*/
|
|
179
185
|
async at(hash) {
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
const cached = this._apiAtCache.get(hash);
|
|
187
|
+
if (cached)
|
|
188
|
+
return cached;
|
|
182
189
|
let targetVersion;
|
|
183
190
|
// Try to get block info from ChainHead first (for pinned blocks)
|
|
184
191
|
const targetBlock = this.chainHead.findBlock(hash);
|
|
@@ -192,10 +199,10 @@ class DedotClient// prettier-end-here
|
|
|
192
199
|
else {
|
|
193
200
|
// Block not pinned, try via Archive fallback if supported
|
|
194
201
|
if (this._archive && (await this._archive.supported())) {
|
|
195
|
-
console.warn(`Block ${hash} is not pinned, using Archive for historical access`);
|
|
196
202
|
try {
|
|
197
203
|
// Fetch runtime version via Archive
|
|
198
204
|
const runtimeRaw = await this._archive.call('Core_version', '0x', hash);
|
|
205
|
+
(0, utils_1.assert)(runtimeRaw, 'Runtime Version Not Found');
|
|
199
206
|
targetVersion = this.toSubstrateRuntimeVersion(codecs_1.$RuntimeVersion.tryDecode(runtimeRaw));
|
|
200
207
|
}
|
|
201
208
|
catch (error) {
|
|
@@ -228,7 +235,7 @@ class DedotClient// prettier-end-here
|
|
|
228
235
|
api.query = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.StorageQueryExecutorV2(api, this.chainHead) });
|
|
229
236
|
api.call = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.RuntimeApiExecutorV2(api, this.chainHead) });
|
|
230
237
|
api.view = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.ViewFunctionExecutorV2(api, this.chainHead) });
|
|
231
|
-
this
|
|
238
|
+
this._apiAtCache.set(hash, api);
|
|
232
239
|
return api;
|
|
233
240
|
}
|
|
234
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
|
-
|
|
236
|
-
|
|
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
|
|
258
|
+
this._apiAtCache.set(hash, api);
|
|
260
259
|
return api;
|
|
261
260
|
}
|
|
262
261
|
getStorageQuery() {
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Known signed extensions that require external input.
|
|
4
|
-
*
|
|
5
|
-
* Note: Extensions that don't require external input are automatically handled by FallbackSignedExtension:
|
|
6
|
-
* - CheckNonZeroSender
|
|
7
|
-
* - CheckWeight
|
|
8
|
-
* - PrevalidateAttests
|
|
9
|
-
* - StorageWeightReclaim
|
|
10
|
-
*
|
|
11
|
-
* These extensions have empty struct or tuple types and don't need explicit implementation.
|
|
12
|
-
*/
|
|
13
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
3
|
exports.knownSignedExtensions = void 0;
|
|
15
4
|
const ChargeAssetTxPayment_js_1 = require("./ChargeAssetTxPayment.js");
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Archive = void 0;
|
|
4
4
|
const utils_1 = require("@dedot/utils");
|
|
5
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
|
|
6
8
|
/**
|
|
7
9
|
* @name Archive
|
|
8
10
|
* Archive JSON-RPC methods for accessing historical blockchain data.
|
|
@@ -16,7 +18,7 @@ class Archive extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
16
18
|
#cache;
|
|
17
19
|
constructor(client, options) {
|
|
18
20
|
super(client, { prefix: 'archive', supportedVersions: ['unstable', 'v1'], ...options });
|
|
19
|
-
this.#cache = new
|
|
21
|
+
this.#cache = new utils_1.LRUCache(ARCHIVE_CACHE_CAPACITY, ARCHIVE_CACHE_TTL);
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* Retrieves the body (list of transactions) of a given block.
|
|
@@ -38,8 +40,9 @@ class Archive extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
38
40
|
async body(hash) {
|
|
39
41
|
const blockHash = hash || (await this.finalizedHash());
|
|
40
42
|
const cacheKey = `${blockHash}::body`;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const cached = this.#cache.get(cacheKey);
|
|
44
|
+
if (cached !== null) {
|
|
45
|
+
return cached;
|
|
43
46
|
}
|
|
44
47
|
const result = await this.send('body', blockHash);
|
|
45
48
|
this.#cache.set(cacheKey, result);
|
|
@@ -77,8 +80,9 @@ class Archive extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
77
80
|
async header(hash) {
|
|
78
81
|
const blockHash = hash || (await this.finalizedHash());
|
|
79
82
|
const cacheKey = `${blockHash}::header`;
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
const cached = this.#cache.get(cacheKey);
|
|
84
|
+
if (cached !== null) {
|
|
85
|
+
return cached;
|
|
82
86
|
}
|
|
83
87
|
const result = await this.send('header', blockHash);
|
|
84
88
|
this.#cache.set(cacheKey, result);
|
|
@@ -141,8 +145,9 @@ class Archive extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
141
145
|
async call(func, params, hash) {
|
|
142
146
|
const blockHash = hash || (await this.finalizedHash());
|
|
143
147
|
const cacheKey = `${blockHash}::call::${func}::${params}`;
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
const cached = this.#cache.get(cacheKey);
|
|
149
|
+
if (cached !== null) {
|
|
150
|
+
return cached;
|
|
146
151
|
}
|
|
147
152
|
const result = await this.send('call', blockHash, func, params);
|
|
148
153
|
if (!result.success) {
|
|
@@ -189,8 +194,9 @@ class Archive extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
189
194
|
// Generate cache key
|
|
190
195
|
const cacheKey = `${blockHash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
|
|
191
196
|
// Check cache
|
|
192
|
-
|
|
193
|
-
|
|
197
|
+
const cached = this.#cache.get(cacheKey);
|
|
198
|
+
if (cached !== null) {
|
|
199
|
+
return resolve(cached);
|
|
194
200
|
}
|
|
195
201
|
this.#storageSubscription(items, childTrie || null, (event) => {
|
|
196
202
|
switch (event.event) {
|
|
@@ -7,6 +7,8 @@ const JsonRpcGroup_js_1 = require("../JsonRpcGroup.js");
|
|
|
7
7
|
const BlockUsage_js_1 = require("./BlockUsage.js");
|
|
8
8
|
const error_js_1 = require("./error.js");
|
|
9
9
|
exports.MIN_FINALIZED_QUEUE_SIZE = 10; // finalized queue size
|
|
10
|
+
const CHAINHEAD_CACHE_CAPACITY = 256;
|
|
11
|
+
const CHAINHEAD_CACHE_TTL = 30_000; // 30 seconds
|
|
10
12
|
class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
11
13
|
#unsub;
|
|
12
14
|
#subscriptionId;
|
|
@@ -43,7 +45,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
43
45
|
this.#followResponseQueue = new utils_1.AsyncQueue();
|
|
44
46
|
this.#retryQueue = new utils_1.AsyncQueue();
|
|
45
47
|
this.#blockUsage = new BlockUsage_js_1.BlockUsage();
|
|
46
|
-
this.#cache = new
|
|
48
|
+
this.#cache = new utils_1.LRUCache(CHAINHEAD_CACHE_CAPACITY, CHAINHEAD_CACHE_TTL);
|
|
47
49
|
// This helps us to not accidentally putting too much stress on the JSON-RPC server, especially smoldot/light-client
|
|
48
50
|
this.#operationQueue = new utils_1.ThrottleQueue(this.#__unsafe__isSmoldot() ? 25 : 250);
|
|
49
51
|
}
|
|
@@ -245,8 +247,10 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
245
247
|
if (!this.isPinned(hash))
|
|
246
248
|
return;
|
|
247
249
|
delete this.#pinnedBlocks[hash];
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
+
// Clear cache entries related to the pruned block
|
|
251
|
+
// Filter and remove only cache entries for this specific block
|
|
252
|
+
this.#cache
|
|
253
|
+
.keys()
|
|
250
254
|
.filter((key) => key.startsWith(`${hash}::`))
|
|
251
255
|
.forEach((key) => this.#cache.delete(key));
|
|
252
256
|
});
|
|
@@ -394,7 +398,6 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
394
398
|
catch (error) {
|
|
395
399
|
if (error instanceof error_js_1.ChainHeadBlockNotPinnedError && this.#archive) {
|
|
396
400
|
const errorHash = error.hash;
|
|
397
|
-
console.warn(`Block ${errorHash} not pinned in ChainHead, falling back to Archive`);
|
|
398
401
|
return await fallback(this.#archive, errorHash);
|
|
399
402
|
}
|
|
400
403
|
throw error;
|
|
@@ -454,7 +457,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
454
457
|
this.#followResponseQueue.clear();
|
|
455
458
|
this.#retryQueue.clear();
|
|
456
459
|
this.#blockUsage.clear();
|
|
457
|
-
this
|
|
460
|
+
this.clearCache();
|
|
458
461
|
this.#operationQueue.cancel();
|
|
459
462
|
}
|
|
460
463
|
async #ensureFollowed() {
|
|
@@ -532,8 +535,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
532
535
|
const operation = async () => {
|
|
533
536
|
const atHash = this.#ensurePinnedHash(at);
|
|
534
537
|
const cacheKey = `${atHash}::body`;
|
|
535
|
-
|
|
536
|
-
|
|
538
|
+
const cached = this.#cache.get(cacheKey);
|
|
539
|
+
if (cached) {
|
|
540
|
+
return cached;
|
|
537
541
|
}
|
|
538
542
|
const bodyOperation = async () => {
|
|
539
543
|
await this.#ensureFollowed();
|
|
@@ -571,8 +575,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
571
575
|
const operation = async () => {
|
|
572
576
|
const atHash = this.#ensurePinnedHash(at);
|
|
573
577
|
const cacheKey = `${atHash}::call::${func}::${params}`;
|
|
574
|
-
|
|
575
|
-
|
|
578
|
+
const cached = this.#cache.get(cacheKey);
|
|
579
|
+
if (cached) {
|
|
580
|
+
return cached;
|
|
576
581
|
}
|
|
577
582
|
const callOperation = async () => {
|
|
578
583
|
await this.#ensureFollowed();
|
|
@@ -603,8 +608,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
603
608
|
const operation = async () => {
|
|
604
609
|
const hash = this.#ensurePinnedHash(at);
|
|
605
610
|
const cacheKey = `${hash}::header`;
|
|
606
|
-
|
|
607
|
-
|
|
611
|
+
const cached = this.#cache.get(cacheKey);
|
|
612
|
+
if (cached) {
|
|
613
|
+
return cached;
|
|
608
614
|
}
|
|
609
615
|
const resp = await this.#getHeader(hash);
|
|
610
616
|
this.#cache.set(cacheKey, resp);
|
|
@@ -627,8 +633,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
627
633
|
try {
|
|
628
634
|
// JSON.stringify(items) might get big, we probably should do a twox hashing in such case
|
|
629
635
|
const cacheKey = `${hash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
|
|
630
|
-
|
|
631
|
-
|
|
636
|
+
const cached = this.#cache.get(cacheKey);
|
|
637
|
+
if (cached) {
|
|
638
|
+
return cached;
|
|
632
639
|
}
|
|
633
640
|
this.#blockUsage.use(hash);
|
|
634
641
|
let results = [];
|
|
@@ -713,5 +720,19 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
713
720
|
// @ts-ignore a trick internally to check whether a provider is using smoldot connection
|
|
714
721
|
return typeof this.client.provider['chain'] === 'function';
|
|
715
722
|
}
|
|
723
|
+
/**
|
|
724
|
+
* Clears the internal cache used for storing query results (both chainHead & archive instances)
|
|
725
|
+
* This can be useful for memory management or when you want to force fresh data retrieval.
|
|
726
|
+
*
|
|
727
|
+
* @example
|
|
728
|
+
* ```typescript
|
|
729
|
+
* // Clear all cached results
|
|
730
|
+
* chainHead.clearCache();
|
|
731
|
+
* ```
|
|
732
|
+
*/
|
|
733
|
+
clearCache() {
|
|
734
|
+
this.#cache.clear();
|
|
735
|
+
this.#archive?.clearCache();
|
|
736
|
+
}
|
|
716
737
|
}
|
|
717
738
|
exports.ChainHead = ChainHead;
|
|
@@ -2,7 +2,7 @@ import { BlockHash, Hash, Metadata, PortableRegistry, RuntimeVersion } from '@de
|
|
|
2
2
|
import type { JsonRpcProvider } from '@dedot/providers';
|
|
3
3
|
import { type IStorage } from '@dedot/storage';
|
|
4
4
|
import { Callback, GenericStorageQuery, GenericSubstrateApi, InjectedSigner, Query, QueryFnResult, RpcVersion, Unsub, VersionedGenericSubstrateApi } from '@dedot/types';
|
|
5
|
-
import { Deferred } from '@dedot/utils';
|
|
5
|
+
import { Deferred, LRUCache } from '@dedot/utils';
|
|
6
6
|
import type { SubstrateApi } from '../chaintypes/index.js';
|
|
7
7
|
import { JsonRpcClient } from '../json-rpc/index.js';
|
|
8
8
|
import { BaseStorageQuery } from '../storage/index.js';
|
|
@@ -15,12 +15,13 @@ export declare function ensurePresence<T>(value: T): NonNullable<T>;
|
|
|
15
15
|
export declare abstract class BaseSubstrateClient<Rv extends RpcVersion, ChainApi extends VersionedGenericSubstrateApi = SubstrateApi, Events extends string = ApiEvent> extends JsonRpcClient<ChainApi, Events> implements ISubstrateClient<ChainApi[Rv], Events> {
|
|
16
16
|
rpcVersion: RpcVersion;
|
|
17
17
|
protected _options: ApiOptions;
|
|
18
|
-
protected _registry?: PortableRegistry<ChainApi[Rv]>;
|
|
18
|
+
protected _registry?: PortableRegistry<ChainApi[Rv]['types']>;
|
|
19
19
|
protected _metadata?: Metadata;
|
|
20
20
|
protected _genesisHash?: Hash;
|
|
21
21
|
protected _runtimeVersion?: SubstrateRuntimeVersion;
|
|
22
22
|
protected _localCache?: IStorage;
|
|
23
23
|
protected _runtimeUpgrading?: Deferred<void>;
|
|
24
|
+
protected _apiAtCache: LRUCache;
|
|
24
25
|
protected constructor(rpcVersion: RpcVersion, options: JsonRpcClientOptions | JsonRpcProvider);
|
|
25
26
|
protected normalizeOptions(options: ApiOptions | JsonRpcProvider): ApiOptions;
|
|
26
27
|
protected initializeLocalCache(): Promise<void>;
|
|
@@ -37,9 +38,10 @@ export declare abstract class BaseSubstrateClient<Rv extends RpcVersion, ChainAp
|
|
|
37
38
|
protected fetchMetadata(hash?: BlockHash, runtime?: SubstrateRuntimeVersion): Promise<Metadata>;
|
|
38
39
|
protected cleanUp(): void;
|
|
39
40
|
/**
|
|
40
|
-
* @description Clear local cache
|
|
41
|
+
* @description Clear local cache and API at-block cache
|
|
42
|
+
* @param keepMetadataCache Keep the metadata cache, only clear other caches.
|
|
41
43
|
*/
|
|
42
|
-
clearCache(): Promise<void>;
|
|
44
|
+
clearCache(keepMetadataCache?: boolean): Promise<void>;
|
|
43
45
|
protected doConnect(): Promise<this>;
|
|
44
46
|
protected onConnected: () => Promise<void>;
|
|
45
47
|
protected onDisconnected: () => Promise<void>;
|
|
@@ -61,7 +63,7 @@ export declare abstract class BaseSubstrateClient<Rv extends RpcVersion, ChainAp
|
|
|
61
63
|
disconnect(): Promise<void>;
|
|
62
64
|
get options(): ApiOptions;
|
|
63
65
|
get metadata(): Metadata;
|
|
64
|
-
get registry(): PortableRegistry<ChainApi[Rv]>;
|
|
66
|
+
get registry(): PortableRegistry<ChainApi[Rv]['types']>;
|
|
65
67
|
get genesisHash(): Hash;
|
|
66
68
|
get runtimeVersion(): SubstrateRuntimeVersion;
|
|
67
69
|
getRuntimeVersion(): Promise<SubstrateRuntimeVersion>;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { $Metadata, PortableRegistry } from '@dedot/codecs';
|
|
2
2
|
import { LocalStorage } from '@dedot/storage';
|
|
3
|
-
import { calcRuntimeApiHash, deferred, ensurePresence as _ensurePresence, u8aToHex } from '@dedot/utils';
|
|
3
|
+
import { calcRuntimeApiHash, deferred, ensurePresence as _ensurePresence, u8aToHex, LRUCache, } from '@dedot/utils';
|
|
4
4
|
import { ConstantExecutor, ErrorExecutor, EventExecutor } from '../executor/index.js';
|
|
5
5
|
import { isJsonRpcProvider, JsonRpcClient } from '../json-rpc/index.js';
|
|
6
6
|
import { newProxyChain } from '../proxychain.js';
|
|
7
7
|
import { QueryableStorage } from '../storage/index.js';
|
|
8
8
|
const SUPPORTED_METADATA_VERSIONS = [16, 15, 14];
|
|
9
9
|
const MetadataApiHash = calcRuntimeApiHash('Metadata'); // 0x37e397fc7c91f5e4
|
|
10
|
+
const API_AT_CACHE_CAPACITY = 64;
|
|
11
|
+
const API_AT_CACHE_TTL = 300_000; // 5 minutes
|
|
10
12
|
const MESSAGE = 'Make sure to call `.connect()` method first before using the API interfaces.';
|
|
11
13
|
export function ensurePresence(value) {
|
|
12
14
|
return _ensurePresence(value, MESSAGE);
|
|
@@ -24,10 +26,12 @@ export class BaseSubstrateClient extends JsonRpcClient {
|
|
|
24
26
|
_runtimeVersion;
|
|
25
27
|
_localCache;
|
|
26
28
|
_runtimeUpgrading;
|
|
29
|
+
_apiAtCache;
|
|
27
30
|
constructor(rpcVersion, options) {
|
|
28
31
|
super(options);
|
|
29
32
|
this.rpcVersion = rpcVersion;
|
|
30
33
|
this._options = this.normalizeOptions(options);
|
|
34
|
+
this._apiAtCache = new LRUCache(API_AT_CACHE_CAPACITY, API_AT_CACHE_TTL);
|
|
31
35
|
}
|
|
32
36
|
/// --- Internal logics
|
|
33
37
|
normalizeOptions(options) {
|
|
@@ -185,12 +189,17 @@ export class BaseSubstrateClient extends JsonRpcClient {
|
|
|
185
189
|
this._genesisHash = undefined;
|
|
186
190
|
this._runtimeVersion = undefined;
|
|
187
191
|
this._localCache = undefined;
|
|
192
|
+
this._apiAtCache.clear();
|
|
188
193
|
}
|
|
189
194
|
/**
|
|
190
|
-
* @description Clear local cache
|
|
195
|
+
* @description Clear local cache and API at-block cache
|
|
196
|
+
* @param keepMetadataCache Keep the metadata cache, only clear other caches.
|
|
191
197
|
*/
|
|
192
|
-
async clearCache() {
|
|
193
|
-
|
|
198
|
+
async clearCache(keepMetadataCache = false) {
|
|
199
|
+
if (!keepMetadataCache) {
|
|
200
|
+
await this._localCache?.clear();
|
|
201
|
+
}
|
|
202
|
+
this._apiAtCache.clear();
|
|
194
203
|
}
|
|
195
204
|
async doConnect() {
|
|
196
205
|
// @ts-ignore
|
package/client/DedotClient.d.ts
CHANGED
|
@@ -50,6 +50,11 @@ export declare class DedotClient<ChainApi extends VersionedGenericSubstrateApi =
|
|
|
50
50
|
protected beforeDisconnect(): Promise<void>;
|
|
51
51
|
protected onDisconnected: () => Promise<void>;
|
|
52
52
|
protected cleanUp(): void;
|
|
53
|
+
/**
|
|
54
|
+
* @description Clear local cache, API at-block cache, and ChainHead cache
|
|
55
|
+
* @param keepMetadataCache Keep the metadata cache, only clear other caches.
|
|
56
|
+
*/
|
|
57
|
+
clearCache(keepMetadataCache?: boolean): Promise<void>;
|
|
53
58
|
get query(): ChainApi[RpcV2]['query'];
|
|
54
59
|
get view(): ChainApi[RpcV2]['view'];
|
|
55
60
|
get call(): ChainApi[RpcV2]['call'];
|
package/client/DedotClient.js
CHANGED
|
@@ -18,7 +18,6 @@ export class DedotClient// prettier-end-here
|
|
|
18
18
|
_chainSpec;
|
|
19
19
|
_archive;
|
|
20
20
|
_txBroadcaster;
|
|
21
|
-
#apiAtCache = {};
|
|
22
21
|
/**
|
|
23
22
|
* Use factory methods (`create`, `new`) to create `DedotClient` instances.
|
|
24
23
|
*
|
|
@@ -144,7 +143,14 @@ export class DedotClient// prettier-end-here
|
|
|
144
143
|
this._chainSpec = undefined;
|
|
145
144
|
this._archive = undefined;
|
|
146
145
|
this._txBroadcaster = undefined;
|
|
147
|
-
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @description Clear local cache, API at-block cache, and ChainHead cache
|
|
149
|
+
* @param keepMetadataCache Keep the metadata cache, only clear other caches.
|
|
150
|
+
*/
|
|
151
|
+
async clearCache(keepMetadataCache = false) {
|
|
152
|
+
await super.clearCache(keepMetadataCache);
|
|
153
|
+
this._chainHead?.clearCache();
|
|
148
154
|
}
|
|
149
155
|
get query() {
|
|
150
156
|
return newProxyChain({
|
|
@@ -174,8 +180,9 @@ export class DedotClient// prettier-end-here
|
|
|
174
180
|
* @param hash
|
|
175
181
|
*/
|
|
176
182
|
async at(hash) {
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
const cached = this._apiAtCache.get(hash);
|
|
184
|
+
if (cached)
|
|
185
|
+
return cached;
|
|
179
186
|
let targetVersion;
|
|
180
187
|
// Try to get block info from ChainHead first (for pinned blocks)
|
|
181
188
|
const targetBlock = this.chainHead.findBlock(hash);
|
|
@@ -189,10 +196,10 @@ export class DedotClient// prettier-end-here
|
|
|
189
196
|
else {
|
|
190
197
|
// Block not pinned, try via Archive fallback if supported
|
|
191
198
|
if (this._archive && (await this._archive.supported())) {
|
|
192
|
-
console.warn(`Block ${hash} is not pinned, using Archive for historical access`);
|
|
193
199
|
try {
|
|
194
200
|
// Fetch runtime version via Archive
|
|
195
201
|
const runtimeRaw = await this._archive.call('Core_version', '0x', hash);
|
|
202
|
+
assert(runtimeRaw, 'Runtime Version Not Found');
|
|
196
203
|
targetVersion = this.toSubstrateRuntimeVersion($RuntimeVersion.tryDecode(runtimeRaw));
|
|
197
204
|
}
|
|
198
205
|
catch (error) {
|
|
@@ -225,7 +232,7 @@ export class DedotClient// prettier-end-here
|
|
|
225
232
|
api.query = newProxyChain({ executor: new StorageQueryExecutorV2(api, this.chainHead) });
|
|
226
233
|
api.call = newProxyChain({ executor: new RuntimeApiExecutorV2(api, this.chainHead) });
|
|
227
234
|
api.view = newProxyChain({ executor: new ViewFunctionExecutorV2(api, this.chainHead) });
|
|
228
|
-
this
|
|
235
|
+
this._apiAtCache.set(hash, api);
|
|
229
236
|
return api;
|
|
230
237
|
}
|
|
231
238
|
getStorageQuery() {
|
package/client/LegacyClient.js
CHANGED
|
@@ -46,7 +46,6 @@ export class LegacyClient// prettier-end-here
|
|
|
46
46
|
extends BaseSubstrateClient {
|
|
47
47
|
#runtimeSubscriptionUnsub;
|
|
48
48
|
#healthTimer;
|
|
49
|
-
#apiAtCache = {};
|
|
50
49
|
/**
|
|
51
50
|
* Use factory methods (`create`, `new`) to create `Dedot` instances.
|
|
52
51
|
*
|
|
@@ -93,7 +92,6 @@ export class LegacyClient// prettier-end-here
|
|
|
93
92
|
}
|
|
94
93
|
cleanUp() {
|
|
95
94
|
super.cleanUp();
|
|
96
|
-
this.#apiAtCache = {};
|
|
97
95
|
this.#healthTimer = undefined;
|
|
98
96
|
this.#runtimeSubscriptionUnsub = undefined;
|
|
99
97
|
}
|
|
@@ -229,8 +227,9 @@ export class LegacyClient// prettier-end-here
|
|
|
229
227
|
* @param hash
|
|
230
228
|
*/
|
|
231
229
|
async at(hash) {
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
const cached = this._apiAtCache.get(hash);
|
|
231
|
+
if (cached)
|
|
232
|
+
return cached;
|
|
234
233
|
const targetVersion = await this.#getRuntimeVersion(hash);
|
|
235
234
|
let metadata = this.metadata;
|
|
236
235
|
let registry = this.registry;
|
|
@@ -253,7 +252,7 @@ export class LegacyClient// prettier-end-here
|
|
|
253
252
|
api.call = newProxyChain({ executor: new RuntimeApiExecutor(api) });
|
|
254
253
|
api.events = newProxyChain({ executor: new EventExecutor(api) });
|
|
255
254
|
api.errors = newProxyChain({ executor: new ErrorExecutor(api) });
|
|
256
|
-
this
|
|
255
|
+
this._apiAtCache.set(hash, api);
|
|
257
256
|
return api;
|
|
258
257
|
}
|
|
259
258
|
getStorageQuery() {
|
package/executor/Executor.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare abstract class Executor<ChainApi extends GenericSubstrateApi = Ge
|
|
|
16
16
|
readonly client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>;
|
|
17
17
|
constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, atBlockHash?: BlockHash);
|
|
18
18
|
get atBlockHash(): any;
|
|
19
|
-
get registry(): import("@dedot/codecs").PortableRegistry<ChainApi>;
|
|
19
|
+
get registry(): import("@dedot/codecs").PortableRegistry<ChainApi["types"]>;
|
|
20
20
|
get metadata(): {
|
|
21
21
|
types: {
|
|
22
22
|
id: number;
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Known signed extensions that require external input.
|
|
3
|
-
*
|
|
4
|
-
* Note: Extensions that don't require external input are automatically handled by FallbackSignedExtension:
|
|
5
|
-
* - CheckNonZeroSender
|
|
6
|
-
* - CheckWeight
|
|
7
|
-
* - PrevalidateAttests
|
|
8
|
-
* - StorageWeightReclaim
|
|
9
|
-
*
|
|
10
|
-
* These extensions have empty struct or tuple types and don't need explicit implementation.
|
|
11
|
-
*/
|
|
12
1
|
import { ChargeAssetTxPayment } from './ChargeAssetTxPayment.js';
|
|
13
2
|
import { ChargeTransactionPayment } from './ChargeTransactionPayment.js';
|
|
14
3
|
import { CheckGenesis } from './CheckGenesis.js';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { DedotError } from '@dedot/utils';
|
|
1
|
+
import { DedotError, LRUCache } from '@dedot/utils';
|
|
2
2
|
import { JsonRpcGroup } from './JsonRpcGroup.js';
|
|
3
|
+
const ARCHIVE_CACHE_CAPACITY = 256;
|
|
4
|
+
const ARCHIVE_CACHE_TTL = 60_000; // 1 minutes - archive data is immutable
|
|
3
5
|
/**
|
|
4
6
|
* @name Archive
|
|
5
7
|
* Archive JSON-RPC methods for accessing historical blockchain data.
|
|
@@ -13,7 +15,7 @@ export class Archive extends JsonRpcGroup {
|
|
|
13
15
|
#cache;
|
|
14
16
|
constructor(client, options) {
|
|
15
17
|
super(client, { prefix: 'archive', supportedVersions: ['unstable', 'v1'], ...options });
|
|
16
|
-
this.#cache = new
|
|
18
|
+
this.#cache = new LRUCache(ARCHIVE_CACHE_CAPACITY, ARCHIVE_CACHE_TTL);
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Retrieves the body (list of transactions) of a given block.
|
|
@@ -35,8 +37,9 @@ export class Archive extends JsonRpcGroup {
|
|
|
35
37
|
async body(hash) {
|
|
36
38
|
const blockHash = hash || (await this.finalizedHash());
|
|
37
39
|
const cacheKey = `${blockHash}::body`;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
const cached = this.#cache.get(cacheKey);
|
|
41
|
+
if (cached !== null) {
|
|
42
|
+
return cached;
|
|
40
43
|
}
|
|
41
44
|
const result = await this.send('body', blockHash);
|
|
42
45
|
this.#cache.set(cacheKey, result);
|
|
@@ -74,8 +77,9 @@ export class Archive extends JsonRpcGroup {
|
|
|
74
77
|
async header(hash) {
|
|
75
78
|
const blockHash = hash || (await this.finalizedHash());
|
|
76
79
|
const cacheKey = `${blockHash}::header`;
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
const cached = this.#cache.get(cacheKey);
|
|
81
|
+
if (cached !== null) {
|
|
82
|
+
return cached;
|
|
79
83
|
}
|
|
80
84
|
const result = await this.send('header', blockHash);
|
|
81
85
|
this.#cache.set(cacheKey, result);
|
|
@@ -138,8 +142,9 @@ export class Archive extends JsonRpcGroup {
|
|
|
138
142
|
async call(func, params, hash) {
|
|
139
143
|
const blockHash = hash || (await this.finalizedHash());
|
|
140
144
|
const cacheKey = `${blockHash}::call::${func}::${params}`;
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
const cached = this.#cache.get(cacheKey);
|
|
146
|
+
if (cached !== null) {
|
|
147
|
+
return cached;
|
|
143
148
|
}
|
|
144
149
|
const result = await this.send('call', blockHash, func, params);
|
|
145
150
|
if (!result.success) {
|
|
@@ -186,8 +191,9 @@ export class Archive extends JsonRpcGroup {
|
|
|
186
191
|
// Generate cache key
|
|
187
192
|
const cacheKey = `${blockHash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
|
|
188
193
|
// Check cache
|
|
189
|
-
|
|
190
|
-
|
|
194
|
+
const cached = this.#cache.get(cacheKey);
|
|
195
|
+
if (cached !== null) {
|
|
196
|
+
return resolve(cached);
|
|
191
197
|
}
|
|
192
198
|
this.#storageSubscription(items, childTrie || null, (event) => {
|
|
193
199
|
switch (event.event) {
|
|
@@ -77,4 +77,15 @@ export declare class ChainHead extends JsonRpcGroup<ChainHeadEvent> {
|
|
|
77
77
|
* @protected
|
|
78
78
|
*/
|
|
79
79
|
protected unpin(hashes: BlockHash | BlockHash[]): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Clears the internal cache used for storing query results (both chainHead & archive instances)
|
|
82
|
+
* This can be useful for memory management or when you want to force fresh data retrieval.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* // Clear all cached results
|
|
87
|
+
* chainHead.clearCache();
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
clearCache(): void;
|
|
80
91
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { $Header } from '@dedot/codecs';
|
|
2
|
-
import { assert, AsyncQueue, deferred, ensurePresence, noop, ThrottleQueue, waitFor, } from '@dedot/utils';
|
|
2
|
+
import { assert, AsyncQueue, deferred, ensurePresence, noop, ThrottleQueue, waitFor, LRUCache, } from '@dedot/utils';
|
|
3
3
|
import { JsonRpcGroup } from '../JsonRpcGroup.js';
|
|
4
4
|
import { BlockUsage } from './BlockUsage.js';
|
|
5
5
|
import { ChainHeadBlockNotPinnedError, ChainHeadBlockPrunedError, ChainHeadError, ChainHeadLimitReachedError, ChainHeadOperationError, ChainHeadOperationInaccessibleError, ChainHeadStopError, RetryStrategy, } from './error.js';
|
|
6
6
|
export const MIN_FINALIZED_QUEUE_SIZE = 10; // finalized queue size
|
|
7
|
+
const CHAINHEAD_CACHE_CAPACITY = 256;
|
|
8
|
+
const CHAINHEAD_CACHE_TTL = 30_000; // 30 seconds
|
|
7
9
|
export class ChainHead extends JsonRpcGroup {
|
|
8
10
|
#unsub;
|
|
9
11
|
#subscriptionId;
|
|
@@ -40,7 +42,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
40
42
|
this.#followResponseQueue = new AsyncQueue();
|
|
41
43
|
this.#retryQueue = new AsyncQueue();
|
|
42
44
|
this.#blockUsage = new BlockUsage();
|
|
43
|
-
this.#cache = new
|
|
45
|
+
this.#cache = new LRUCache(CHAINHEAD_CACHE_CAPACITY, CHAINHEAD_CACHE_TTL);
|
|
44
46
|
// This helps us to not accidentally putting too much stress on the JSON-RPC server, especially smoldot/light-client
|
|
45
47
|
this.#operationQueue = new ThrottleQueue(this.#__unsafe__isSmoldot() ? 25 : 250);
|
|
46
48
|
}
|
|
@@ -242,8 +244,10 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
242
244
|
if (!this.isPinned(hash))
|
|
243
245
|
return;
|
|
244
246
|
delete this.#pinnedBlocks[hash];
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
+
// Clear cache entries related to the pruned block
|
|
248
|
+
// Filter and remove only cache entries for this specific block
|
|
249
|
+
this.#cache
|
|
250
|
+
.keys()
|
|
247
251
|
.filter((key) => key.startsWith(`${hash}::`))
|
|
248
252
|
.forEach((key) => this.#cache.delete(key));
|
|
249
253
|
});
|
|
@@ -391,7 +395,6 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
391
395
|
catch (error) {
|
|
392
396
|
if (error instanceof ChainHeadBlockNotPinnedError && this.#archive) {
|
|
393
397
|
const errorHash = error.hash;
|
|
394
|
-
console.warn(`Block ${errorHash} not pinned in ChainHead, falling back to Archive`);
|
|
395
398
|
return await fallback(this.#archive, errorHash);
|
|
396
399
|
}
|
|
397
400
|
throw error;
|
|
@@ -451,7 +454,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
451
454
|
this.#followResponseQueue.clear();
|
|
452
455
|
this.#retryQueue.clear();
|
|
453
456
|
this.#blockUsage.clear();
|
|
454
|
-
this
|
|
457
|
+
this.clearCache();
|
|
455
458
|
this.#operationQueue.cancel();
|
|
456
459
|
}
|
|
457
460
|
async #ensureFollowed() {
|
|
@@ -529,8 +532,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
529
532
|
const operation = async () => {
|
|
530
533
|
const atHash = this.#ensurePinnedHash(at);
|
|
531
534
|
const cacheKey = `${atHash}::body`;
|
|
532
|
-
|
|
533
|
-
|
|
535
|
+
const cached = this.#cache.get(cacheKey);
|
|
536
|
+
if (cached) {
|
|
537
|
+
return cached;
|
|
534
538
|
}
|
|
535
539
|
const bodyOperation = async () => {
|
|
536
540
|
await this.#ensureFollowed();
|
|
@@ -568,8 +572,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
568
572
|
const operation = async () => {
|
|
569
573
|
const atHash = this.#ensurePinnedHash(at);
|
|
570
574
|
const cacheKey = `${atHash}::call::${func}::${params}`;
|
|
571
|
-
|
|
572
|
-
|
|
575
|
+
const cached = this.#cache.get(cacheKey);
|
|
576
|
+
if (cached) {
|
|
577
|
+
return cached;
|
|
573
578
|
}
|
|
574
579
|
const callOperation = async () => {
|
|
575
580
|
await this.#ensureFollowed();
|
|
@@ -600,8 +605,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
600
605
|
const operation = async () => {
|
|
601
606
|
const hash = this.#ensurePinnedHash(at);
|
|
602
607
|
const cacheKey = `${hash}::header`;
|
|
603
|
-
|
|
604
|
-
|
|
608
|
+
const cached = this.#cache.get(cacheKey);
|
|
609
|
+
if (cached) {
|
|
610
|
+
return cached;
|
|
605
611
|
}
|
|
606
612
|
const resp = await this.#getHeader(hash);
|
|
607
613
|
this.#cache.set(cacheKey, resp);
|
|
@@ -624,8 +630,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
624
630
|
try {
|
|
625
631
|
// JSON.stringify(items) might get big, we probably should do a twox hashing in such case
|
|
626
632
|
const cacheKey = `${hash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
|
|
627
|
-
|
|
628
|
-
|
|
633
|
+
const cached = this.#cache.get(cacheKey);
|
|
634
|
+
if (cached) {
|
|
635
|
+
return cached;
|
|
629
636
|
}
|
|
630
637
|
this.#blockUsage.use(hash);
|
|
631
638
|
let results = [];
|
|
@@ -710,4 +717,18 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
710
717
|
// @ts-ignore a trick internally to check whether a provider is using smoldot connection
|
|
711
718
|
return typeof this.client.provider['chain'] === 'function';
|
|
712
719
|
}
|
|
720
|
+
/**
|
|
721
|
+
* Clears the internal cache used for storing query results (both chainHead & archive instances)
|
|
722
|
+
* This can be useful for memory management or when you want to force fresh data retrieval.
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* ```typescript
|
|
726
|
+
* // Clear all cached results
|
|
727
|
+
* chainHead.clearCache();
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
clearCache() {
|
|
731
|
+
this.#cache.clear();
|
|
732
|
+
this.#archive?.clearCache();
|
|
733
|
+
}
|
|
713
734
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dedot/api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1-next.dd70d277.9+dd70d277",
|
|
4
4
|
"description": "A delightful JavaScript/TypeScript client for Polkadot & Substrate",
|
|
5
5
|
"author": "Thang X. Vu <thang@dedot.dev>",
|
|
6
6
|
"homepage": "https://dedot.dev",
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"sideEffects": false,
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@dedot/codecs": "0.
|
|
17
|
-
"@dedot/providers": "0.
|
|
18
|
-
"@dedot/runtime-specs": "0.
|
|
19
|
-
"@dedot/shape": "0.
|
|
20
|
-
"@dedot/storage": "0.
|
|
21
|
-
"@dedot/types": "0.
|
|
22
|
-
"@dedot/utils": "0.
|
|
16
|
+
"@dedot/codecs": "0.16.1-next.dd70d277.9+dd70d277",
|
|
17
|
+
"@dedot/providers": "0.16.1-next.dd70d277.9+dd70d277",
|
|
18
|
+
"@dedot/runtime-specs": "0.16.1-next.dd70d277.9+dd70d277",
|
|
19
|
+
"@dedot/shape": "0.16.1-next.dd70d277.9+dd70d277",
|
|
20
|
+
"@dedot/storage": "0.16.1-next.dd70d277.9+dd70d277",
|
|
21
|
+
"@dedot/types": "0.16.1-next.dd70d277.9+dd70d277",
|
|
22
|
+
"@dedot/utils": "0.16.1-next.dd70d277.9+dd70d277"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=18"
|
|
49
49
|
},
|
|
50
50
|
"license": "Apache-2.0",
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "dd70d2772f30eaa72ec9f61d1af9597211c364c5",
|
|
52
52
|
"module": "./index.js",
|
|
53
53
|
"types": "./index.d.ts"
|
|
54
54
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ISubstrateClient, ISubstrateClientAt } from '@dedot/api/types';
|
|
2
1
|
import { BlockHash, StorageData, StorageKey } from '@dedot/codecs';
|
|
3
2
|
import type { Callback, Unsub } from '@dedot/types';
|
|
3
|
+
import { ISubstrateClient, ISubstrateClientAt } from '../types.js';
|
|
4
4
|
/**
|
|
5
5
|
* @name BaseStorageQuery
|
|
6
6
|
* @description
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DedotClient } from '@dedot/api/client';
|
|
2
1
|
import { BlockHash, StorageData, StorageKey } from '@dedot/codecs';
|
|
3
2
|
import type { Callback, Unsub } from '@dedot/types';
|
|
3
|
+
import { DedotClient } from '../client/DedotClient.js';
|
|
4
4
|
import { BaseStorageQuery } from './BaseStorageQuery.js';
|
|
5
5
|
/**
|
|
6
6
|
* @name NewStorageQuery
|
package/types.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ChainHeadEvent } from '@dedot/api/json-rpc';
|
|
2
1
|
import { BlockHash, Hash, Metadata, PortableRegistry } from '@dedot/codecs';
|
|
3
2
|
import type { ConnectionStatus, JsonRpcProvider, ProviderEvent } from '@dedot/providers';
|
|
4
3
|
import type { AnyShape } from '@dedot/shape';
|
|
@@ -6,6 +5,7 @@ import type { IStorage } from '@dedot/storage';
|
|
|
6
5
|
import type { Callback, GenericStorageQuery, GenericSubstrateApi, InjectedSigner, Query, QueryFnResult, RpcVersion, RuntimeApiName, RuntimeApiSpec, Unsub } from '@dedot/types';
|
|
7
6
|
import type { HashFn, HexString, IEventEmitter } from '@dedot/utils';
|
|
8
7
|
import type { AnySignedExtension } from './extrinsic/index.js';
|
|
8
|
+
import type { ChainHeadEvent } from './json-rpc/index.js';
|
|
9
9
|
export type MetadataKey = `RAW_META/${string}`;
|
|
10
10
|
export type SubscribeMethod = string;
|
|
11
11
|
export type UnsubscribeMethod = string;
|
|
@@ -91,7 +91,7 @@ export interface IGenericSubstrateClient<ChainApi extends GenericSubstrateApi =
|
|
|
91
91
|
genesisHash: Hash;
|
|
92
92
|
runtimeVersion: SubstrateRuntimeVersion;
|
|
93
93
|
metadata: Metadata;
|
|
94
|
-
registry: PortableRegistry<ChainApi>;
|
|
94
|
+
registry: PortableRegistry<ChainApi['types']>;
|
|
95
95
|
rpc: ChainApi['rpc'];
|
|
96
96
|
consts: ChainApi['consts'];
|
|
97
97
|
query: ChainApi['query'];
|