@dedot/api 0.15.3-next.65898ecf.10 → 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.
- package/chaintypes/substrate/index.js +1 -0
- package/cjs/chaintypes/substrate/index.js +1 -0
- package/cjs/client/BaseSubstrateClient.js +12 -3
- package/cjs/client/DedotClient.js +13 -6
- package/cjs/client/LegacyClient.js +4 -5
- package/cjs/json-rpc/group/Archive.js +15 -9
- package/cjs/json-rpc/group/ChainHead/ChainHead.js +33 -13
- package/client/BaseSubstrateClient.d.ts +5 -3
- 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/json-rpc/group/Archive.js +16 -10
- package/json-rpc/group/ChainHead/ChainHead.d.ts +11 -0
- package/json-rpc/group/ChainHead/ChainHead.js +34 -14
- package/package.json +9 -9
- package/storage/BaseStorageQuery.d.ts +1 -1
- package/storage/NewStorageQuery.d.ts +1 -1
- package/types.d.ts +1 -1
|
@@ -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() {
|
|
@@ -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,9 @@ 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.keys()
|
|
250
253
|
.filter((key) => key.startsWith(`${hash}::`))
|
|
251
254
|
.forEach((key) => this.#cache.delete(key));
|
|
252
255
|
});
|
|
@@ -394,7 +397,6 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
394
397
|
catch (error) {
|
|
395
398
|
if (error instanceof error_js_1.ChainHeadBlockNotPinnedError && this.#archive) {
|
|
396
399
|
const errorHash = error.hash;
|
|
397
|
-
console.warn(`Block ${errorHash} not pinned in ChainHead, falling back to Archive`);
|
|
398
400
|
return await fallback(this.#archive, errorHash);
|
|
399
401
|
}
|
|
400
402
|
throw error;
|
|
@@ -454,7 +456,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
454
456
|
this.#followResponseQueue.clear();
|
|
455
457
|
this.#retryQueue.clear();
|
|
456
458
|
this.#blockUsage.clear();
|
|
457
|
-
this
|
|
459
|
+
this.clearCache();
|
|
458
460
|
this.#operationQueue.cancel();
|
|
459
461
|
}
|
|
460
462
|
async #ensureFollowed() {
|
|
@@ -532,8 +534,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
532
534
|
const operation = async () => {
|
|
533
535
|
const atHash = this.#ensurePinnedHash(at);
|
|
534
536
|
const cacheKey = `${atHash}::body`;
|
|
535
|
-
|
|
536
|
-
|
|
537
|
+
const cached = this.#cache.get(cacheKey);
|
|
538
|
+
if (cached) {
|
|
539
|
+
return cached;
|
|
537
540
|
}
|
|
538
541
|
const bodyOperation = async () => {
|
|
539
542
|
await this.#ensureFollowed();
|
|
@@ -571,8 +574,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
571
574
|
const operation = async () => {
|
|
572
575
|
const atHash = this.#ensurePinnedHash(at);
|
|
573
576
|
const cacheKey = `${atHash}::call::${func}::${params}`;
|
|
574
|
-
|
|
575
|
-
|
|
577
|
+
const cached = this.#cache.get(cacheKey);
|
|
578
|
+
if (cached) {
|
|
579
|
+
return cached;
|
|
576
580
|
}
|
|
577
581
|
const callOperation = async () => {
|
|
578
582
|
await this.#ensureFollowed();
|
|
@@ -603,8 +607,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
603
607
|
const operation = async () => {
|
|
604
608
|
const hash = this.#ensurePinnedHash(at);
|
|
605
609
|
const cacheKey = `${hash}::header`;
|
|
606
|
-
|
|
607
|
-
|
|
610
|
+
const cached = this.#cache.get(cacheKey);
|
|
611
|
+
if (cached) {
|
|
612
|
+
return cached;
|
|
608
613
|
}
|
|
609
614
|
const resp = await this.#getHeader(hash);
|
|
610
615
|
this.#cache.set(cacheKey, resp);
|
|
@@ -627,8 +632,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
627
632
|
try {
|
|
628
633
|
// JSON.stringify(items) might get big, we probably should do a twox hashing in such case
|
|
629
634
|
const cacheKey = `${hash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
|
|
630
|
-
|
|
631
|
-
|
|
635
|
+
const cached = this.#cache.get(cacheKey);
|
|
636
|
+
if (cached) {
|
|
637
|
+
return cached;
|
|
632
638
|
}
|
|
633
639
|
this.#blockUsage.use(hash);
|
|
634
640
|
let results = [];
|
|
@@ -713,5 +719,19 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
|
|
|
713
719
|
// @ts-ignore a trick internally to check whether a provider is using smoldot connection
|
|
714
720
|
return typeof this.client.provider['chain'] === 'function';
|
|
715
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* Clears the internal cache used for storing query results (both chainHead & archive instances)
|
|
724
|
+
* This can be useful for memory management or when you want to force fresh data retrieval.
|
|
725
|
+
*
|
|
726
|
+
* @example
|
|
727
|
+
* ```typescript
|
|
728
|
+
* // Clear all cached results
|
|
729
|
+
* chainHead.clearCache();
|
|
730
|
+
* ```
|
|
731
|
+
*/
|
|
732
|
+
clearCache() {
|
|
733
|
+
this.#cache.clear();
|
|
734
|
+
this.#archive?.clearCache();
|
|
735
|
+
}
|
|
716
736
|
}
|
|
717
737
|
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';
|
|
@@ -21,6 +21,7 @@ export declare abstract class BaseSubstrateClient<Rv extends RpcVersion, ChainAp
|
|
|
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>;
|
|
@@ -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() {
|
|
@@ -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,9 @@ 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.keys()
|
|
247
250
|
.filter((key) => key.startsWith(`${hash}::`))
|
|
248
251
|
.forEach((key) => this.#cache.delete(key));
|
|
249
252
|
});
|
|
@@ -391,7 +394,6 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
391
394
|
catch (error) {
|
|
392
395
|
if (error instanceof ChainHeadBlockNotPinnedError && this.#archive) {
|
|
393
396
|
const errorHash = error.hash;
|
|
394
|
-
console.warn(`Block ${errorHash} not pinned in ChainHead, falling back to Archive`);
|
|
395
397
|
return await fallback(this.#archive, errorHash);
|
|
396
398
|
}
|
|
397
399
|
throw error;
|
|
@@ -451,7 +453,7 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
451
453
|
this.#followResponseQueue.clear();
|
|
452
454
|
this.#retryQueue.clear();
|
|
453
455
|
this.#blockUsage.clear();
|
|
454
|
-
this
|
|
456
|
+
this.clearCache();
|
|
455
457
|
this.#operationQueue.cancel();
|
|
456
458
|
}
|
|
457
459
|
async #ensureFollowed() {
|
|
@@ -529,8 +531,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
529
531
|
const operation = async () => {
|
|
530
532
|
const atHash = this.#ensurePinnedHash(at);
|
|
531
533
|
const cacheKey = `${atHash}::body`;
|
|
532
|
-
|
|
533
|
-
|
|
534
|
+
const cached = this.#cache.get(cacheKey);
|
|
535
|
+
if (cached) {
|
|
536
|
+
return cached;
|
|
534
537
|
}
|
|
535
538
|
const bodyOperation = async () => {
|
|
536
539
|
await this.#ensureFollowed();
|
|
@@ -568,8 +571,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
568
571
|
const operation = async () => {
|
|
569
572
|
const atHash = this.#ensurePinnedHash(at);
|
|
570
573
|
const cacheKey = `${atHash}::call::${func}::${params}`;
|
|
571
|
-
|
|
572
|
-
|
|
574
|
+
const cached = this.#cache.get(cacheKey);
|
|
575
|
+
if (cached) {
|
|
576
|
+
return cached;
|
|
573
577
|
}
|
|
574
578
|
const callOperation = async () => {
|
|
575
579
|
await this.#ensureFollowed();
|
|
@@ -600,8 +604,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
600
604
|
const operation = async () => {
|
|
601
605
|
const hash = this.#ensurePinnedHash(at);
|
|
602
606
|
const cacheKey = `${hash}::header`;
|
|
603
|
-
|
|
604
|
-
|
|
607
|
+
const cached = this.#cache.get(cacheKey);
|
|
608
|
+
if (cached) {
|
|
609
|
+
return cached;
|
|
605
610
|
}
|
|
606
611
|
const resp = await this.#getHeader(hash);
|
|
607
612
|
this.#cache.set(cacheKey, resp);
|
|
@@ -624,8 +629,9 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
624
629
|
try {
|
|
625
630
|
// JSON.stringify(items) might get big, we probably should do a twox hashing in such case
|
|
626
631
|
const cacheKey = `${hash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
|
|
627
|
-
|
|
628
|
-
|
|
632
|
+
const cached = this.#cache.get(cacheKey);
|
|
633
|
+
if (cached) {
|
|
634
|
+
return cached;
|
|
629
635
|
}
|
|
630
636
|
this.#blockUsage.use(hash);
|
|
631
637
|
let results = [];
|
|
@@ -710,4 +716,18 @@ export class ChainHead extends JsonRpcGroup {
|
|
|
710
716
|
// @ts-ignore a trick internally to check whether a provider is using smoldot connection
|
|
711
717
|
return typeof this.client.provider['chain'] === 'function';
|
|
712
718
|
}
|
|
719
|
+
/**
|
|
720
|
+
* Clears the internal cache used for storing query results (both chainHead & archive instances)
|
|
721
|
+
* This can be useful for memory management or when you want to force fresh data retrieval.
|
|
722
|
+
*
|
|
723
|
+
* @example
|
|
724
|
+
* ```typescript
|
|
725
|
+
* // Clear all cached results
|
|
726
|
+
* chainHead.clearCache();
|
|
727
|
+
* ```
|
|
728
|
+
*/
|
|
729
|
+
clearCache() {
|
|
730
|
+
this.#cache.clear();
|
|
731
|
+
this.#archive?.clearCache();
|
|
732
|
+
}
|
|
713
733
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dedot/api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
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.0",
|
|
17
|
+
"@dedot/providers": "0.16.0",
|
|
18
|
+
"@dedot/runtime-specs": "0.16.0",
|
|
19
|
+
"@dedot/shape": "0.16.0",
|
|
20
|
+
"@dedot/storage": "0.16.0",
|
|
21
|
+
"@dedot/types": "0.16.0",
|
|
22
|
+
"@dedot/utils": "0.16.0"
|
|
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": "3e78a8eebb8977de0c5fb956154e5b09c4448f31",
|
|
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;
|