@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.
- package/chaintypes/substrate/index.d.ts +21 -1
- package/cjs/client/BaseSubstrateClient.js +12 -3
- package/cjs/client/DedotClient.js +51 -11
- package/cjs/client/LegacyClient.js +4 -5
- package/cjs/executor/Executor.js +1 -0
- package/cjs/executor/v2/StorageQueryExecutorV2.js +1 -3
- package/cjs/executor/v2/TxExecutorV2.js +5 -3
- package/cjs/json-rpc/group/Archive.js +232 -0
- package/cjs/json-rpc/group/ChainHead/ChainHead.js +149 -52
- package/cjs/json-rpc/group/ChainHead/error.js +5 -0
- package/cjs/json-rpc/group/index.js +1 -0
- package/cjs/json-rpc/subscriptionsInfo.js +4 -0
- package/cjs/storage/LegacyStorageQuery.js +5 -0
- package/cjs/storage/NewStorageQuery.js +5 -0
- package/client/BaseSubstrateClient.d.ts +8 -6
- package/client/BaseSubstrateClient.js +13 -4
- package/client/DedotClient.d.ts +11 -4
- package/client/DedotClient.js +54 -14
- package/client/LegacyClient.d.ts +2 -2
- package/client/LegacyClient.js +4 -5
- package/executor/Executor.d.ts +5 -5
- package/executor/Executor.js +1 -0
- package/executor/StorageQueryExecutor.d.ts +2 -2
- package/executor/v2/RuntimeApiExecutorV2.d.ts +2 -2
- package/executor/v2/StorageQueryExecutorV2.d.ts +4 -4
- package/executor/v2/StorageQueryExecutorV2.js +1 -3
- package/executor/v2/TxExecutorV2.d.ts +2 -1
- package/executor/v2/TxExecutorV2.js +5 -3
- package/executor/v2/ViewFunctionExecutorV2.d.ts +2 -2
- package/extrinsic/extensions/SignedExtension.d.ts +3 -3
- package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +2 -2
- package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +2 -2
- package/json-rpc/group/Archive.d.ts +134 -0
- package/json-rpc/group/Archive.js +228 -0
- package/json-rpc/group/ChainHead/ChainHead.d.ts +21 -0
- package/json-rpc/group/ChainHead/ChainHead.js +150 -53
- package/json-rpc/group/ChainHead/error.d.ts +3 -0
- package/json-rpc/group/ChainHead/error.js +5 -0
- package/json-rpc/group/index.d.ts +1 -0
- package/json-rpc/group/index.js +1 -0
- package/json-rpc/subscriptionsInfo.js +4 -0
- package/package.json +9 -9
- package/storage/BaseStorageQuery.d.ts +5 -6
- package/storage/LegacyStorageQuery.d.ts +4 -3
- package/storage/LegacyStorageQuery.js +5 -0
- package/storage/NewStorageQuery.d.ts +4 -3
- package/storage/NewStorageQuery.js +5 -0
- package/storage/QueryableStorage.d.ts +2 -2
- package/types.d.ts +11 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
168
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
|
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
|
-
|
|
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() {
|
package/cjs/executor/Executor.js
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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;
|