@dedot/api 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/chaintypes/substrate/index.d.ts +21 -1
  2. package/cjs/client/BaseSubstrateClient.js +12 -3
  3. package/cjs/client/DedotClient.js +51 -11
  4. package/cjs/client/LegacyClient.js +4 -5
  5. package/cjs/executor/Executor.js +1 -0
  6. package/cjs/executor/v2/StorageQueryExecutorV2.js +1 -3
  7. package/cjs/executor/v2/TxExecutorV2.js +5 -3
  8. package/cjs/json-rpc/group/Archive.js +232 -0
  9. package/cjs/json-rpc/group/ChainHead/ChainHead.js +149 -52
  10. package/cjs/json-rpc/group/ChainHead/error.js +5 -0
  11. package/cjs/json-rpc/group/index.js +1 -0
  12. package/cjs/json-rpc/subscriptionsInfo.js +4 -0
  13. package/cjs/storage/LegacyStorageQuery.js +5 -0
  14. package/cjs/storage/NewStorageQuery.js +5 -0
  15. package/client/BaseSubstrateClient.d.ts +8 -6
  16. package/client/BaseSubstrateClient.js +13 -4
  17. package/client/DedotClient.d.ts +11 -4
  18. package/client/DedotClient.js +54 -14
  19. package/client/LegacyClient.d.ts +2 -2
  20. package/client/LegacyClient.js +4 -5
  21. package/executor/Executor.d.ts +5 -5
  22. package/executor/Executor.js +1 -0
  23. package/executor/StorageQueryExecutor.d.ts +2 -2
  24. package/executor/v2/RuntimeApiExecutorV2.d.ts +2 -2
  25. package/executor/v2/StorageQueryExecutorV2.d.ts +4 -4
  26. package/executor/v2/StorageQueryExecutorV2.js +1 -3
  27. package/executor/v2/TxExecutorV2.d.ts +2 -1
  28. package/executor/v2/TxExecutorV2.js +5 -3
  29. package/executor/v2/ViewFunctionExecutorV2.d.ts +2 -2
  30. package/extrinsic/extensions/SignedExtension.d.ts +3 -3
  31. package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +2 -2
  32. package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +2 -2
  33. package/json-rpc/group/Archive.d.ts +134 -0
  34. package/json-rpc/group/Archive.js +228 -0
  35. package/json-rpc/group/ChainHead/ChainHead.d.ts +21 -0
  36. package/json-rpc/group/ChainHead/ChainHead.js +150 -53
  37. package/json-rpc/group/ChainHead/error.d.ts +3 -0
  38. package/json-rpc/group/ChainHead/error.js +5 -0
  39. package/json-rpc/group/index.d.ts +1 -0
  40. package/json-rpc/group/index.js +1 -0
  41. package/json-rpc/subscriptionsInfo.js +4 -0
  42. package/package.json +9 -9
  43. package/storage/BaseStorageQuery.d.ts +5 -6
  44. package/storage/LegacyStorageQuery.d.ts +4 -3
  45. package/storage/LegacyStorageQuery.js +5 -0
  46. package/storage/NewStorageQuery.d.ts +4 -3
  47. package/storage/NewStorageQuery.js +5 -0
  48. package/storage/QueryableStorage.d.ts +2 -2
  49. package/types.d.ts +11 -6
@@ -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
- if (this.#apiAtCache[hash])
233
- return this.#apiAtCache[hash];
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.#apiAtCache[hash] = api;
255
+ this._apiAtCache.set(hash, api);
257
256
  return api;
258
257
  }
259
258
  getStorageQuery() {
@@ -1,7 +1,7 @@
1
1
  import type { BlockHash, PalletDefLatest } from '@dedot/codecs';
2
2
  import type { GenericSubstrateApi } from '@dedot/types';
3
3
  import { HexString } from '@dedot/utils';
4
- import { ISubstrateClientAt } from '../types.js';
4
+ import { ISubstrateClient, ISubstrateClientAt } from '../types.js';
5
5
  export interface StateCallParams {
6
6
  func: string;
7
7
  params: HexString;
@@ -13,10 +13,10 @@ export interface StateCallParams {
13
13
  */
14
14
  export declare abstract class Executor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> {
15
15
  #private;
16
- readonly client: ISubstrateClientAt<ChainApi>;
17
- constructor(client: ISubstrateClientAt<ChainApi>, atBlockHash?: BlockHash);
18
- get atBlockHash(): `0x${string}` | undefined;
19
- get registry(): import("@dedot/codecs").PortableRegistry;
16
+ readonly client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>;
17
+ constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, atBlockHash?: BlockHash);
18
+ get atBlockHash(): any;
19
+ get registry(): import("@dedot/codecs").PortableRegistry<ChainApi>;
20
20
  get metadata(): {
21
21
  types: {
22
22
  id: number;
@@ -11,6 +11,7 @@ export class Executor {
11
11
  this.#atBlockHash = atBlockHash;
12
12
  }
13
13
  get atBlockHash() {
14
+ // @ts-ignore
14
15
  return this.#atBlockHash || this.client.atBlockHash;
15
16
  }
16
17
  get registry() {
@@ -1,5 +1,5 @@
1
1
  import { BlockHash, Option, StorageData, StorageKey } from '@dedot/codecs';
2
- import type { AsyncMethod, Callback, GenericStorageQuery, GenericSubstrateApi, RpcVersion, Unsub } from '@dedot/types';
2
+ import type { AsyncMethod, Callback, GenericStorageQuery, GenericSubstrateApi, Unsub } from '@dedot/types';
3
3
  import { type BaseStorageQuery, QueryableStorage } from '../storage/index.js';
4
4
  import { Executor } from './Executor.js';
5
5
  /**
@@ -9,7 +9,7 @@ import { Executor } from './Executor.js';
9
9
  export declare class StorageQueryExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
10
10
  doExecute(pallet: string, storage: string): GenericStorageQuery;
11
11
  protected exposeStorageMapMethods(entry: QueryableStorage): Record<string, AsyncMethod>;
12
- protected getStorageQuery(): BaseStorageQuery<RpcVersion>;
12
+ protected getStorageQuery(): BaseStorageQuery;
13
13
  protected queryStorage(keys: StorageKey[], hash?: BlockHash): Promise<Record<StorageKey, Option<StorageData>>>;
14
14
  protected subscribeStorage(keys: StorageKey[], callback: Callback<Array<StorageData | undefined>>): Promise<Unsub>;
15
15
  }
@@ -2,7 +2,7 @@ import type { BlockHash } from '@dedot/codecs';
2
2
  import type { GenericSubstrateApi } from '@dedot/types';
3
3
  import { HexString } from '@dedot/utils';
4
4
  import { ChainHead } from '../../json-rpc/index.js';
5
- import { ISubstrateClientAt } from '../../types.js';
5
+ import { ISubstrateClient, ISubstrateClientAt } from '../../types.js';
6
6
  import { StateCallParams } from '../Executor.js';
7
7
  import { RuntimeApiExecutor } from '../RuntimeApiExecutor.js';
8
8
  /**
@@ -10,6 +10,6 @@ import { RuntimeApiExecutor } from '../RuntimeApiExecutor.js';
10
10
  */
11
11
  export declare class RuntimeApiExecutorV2<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends RuntimeApiExecutor<ChainApi> {
12
12
  chainHead: ChainHead;
13
- constructor(client: ISubstrateClientAt<ChainApi>, chainHead: ChainHead, atBlockHash?: BlockHash);
13
+ constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, chainHead: ChainHead, atBlockHash?: BlockHash);
14
14
  protected stateCall(callParams: StateCallParams): Promise<HexString>;
15
15
  }
@@ -1,15 +1,15 @@
1
1
  import { BlockHash } from '@dedot/codecs';
2
- import type { AsyncMethod, GenericSubstrateApi, RpcVersion } from '@dedot/types';
2
+ import type { AsyncMethod, GenericSubstrateApi } from '@dedot/types';
3
3
  import { ChainHead } from '../../json-rpc/index.js';
4
4
  import { type BaseStorageQuery, QueryableStorage } from '../../storage/index.js';
5
- import { ISubstrateClientAt } from '../../types.js';
5
+ import { ISubstrateClient, ISubstrateClientAt } from '../../types.js';
6
6
  import { StorageQueryExecutor } from '../StorageQueryExecutor.js';
7
7
  /**
8
8
  * @name StorageQueryExecutorV2
9
9
  */
10
10
  export declare class StorageQueryExecutorV2<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends StorageQueryExecutor<ChainApi> {
11
11
  chainHead: ChainHead;
12
- constructor(client: ISubstrateClientAt<ChainApi>, chainHead: ChainHead, atBlockHash?: BlockHash);
12
+ constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, chainHead: ChainHead, atBlockHash?: BlockHash);
13
13
  protected exposeStorageMapMethods(entry: QueryableStorage): Record<string, AsyncMethod>;
14
- protected getStorageQuery(): BaseStorageQuery<RpcVersion>;
14
+ protected getStorageQuery(): BaseStorageQuery;
15
15
  }
@@ -19,9 +19,7 @@ export class StorageQueryExecutorV2 extends StorageQueryExecutor {
19
19
  const entries = async (...args) => {
20
20
  const withArgs = !!args && args.length > 0;
21
21
  const key = withArgs ? entry.encodeKey(args, true) : entry.prefixKey;
22
- const results = await this.chainHead.storage([
23
- { type: 'descendantsValues', key },
24
- ]);
22
+ const results = await this.chainHead.storage([{ type: 'descendantsValues', key }]);
25
23
  return results.map(({ key, value }) => [
26
24
  entry.decodeKey(key),
27
25
  entry.decodeValue(value),
@@ -6,6 +6,7 @@ import { TxExecutor } from '../TxExecutor.js';
6
6
  * @description Execute a transaction instruction, returns a submittable extrinsic
7
7
  */
8
8
  export declare class TxExecutorV2<ChainApi extends VersionedGenericSubstrateApi = VersionedGenericSubstrateApi> extends TxExecutor<ChainApi[RpcVersion]> {
9
- constructor(api: DedotClient<ChainApi>);
9
+ readonly client: DedotClient<ChainApi>;
10
+ constructor(client: DedotClient<ChainApi>);
10
11
  protected createExtrinsic(call: IRuntimeTxCall): any;
11
12
  }
@@ -6,9 +6,11 @@ import { TxExecutor } from '../TxExecutor.js';
6
6
  * @description Execute a transaction instruction, returns a submittable extrinsic
7
7
  */
8
8
  export class TxExecutorV2 extends TxExecutor {
9
- constructor(api) {
10
- assert(api.rpcVersion === 'v2', 'Only supports JSON-RPC v2');
11
- super(api);
9
+ client;
10
+ constructor(client) {
11
+ assert(client.rpcVersion === 'v2', 'Only supports JSON-RPC v2');
12
+ super(client);
13
+ this.client = client;
12
14
  }
13
15
  createExtrinsic(call) {
14
16
  return new SubmittableExtrinsicV2(this.client, call);
@@ -1,7 +1,7 @@
1
1
  import type { GenericSubstrateApi } from '@dedot/types';
2
2
  import { HexString } from '@dedot/utils';
3
3
  import { ChainHead } from '../../json-rpc/index.js';
4
- import { ISubstrateClientAt } from '../../types.js';
4
+ import { ISubstrateClient, ISubstrateClientAt } from '../../types.js';
5
5
  import { StateCallParams } from '../Executor.js';
6
6
  import { ViewFunctionExecutor } from '../ViewFunctionExecutor.js';
7
7
  /**
@@ -9,6 +9,6 @@ import { ViewFunctionExecutor } from '../ViewFunctionExecutor.js';
9
9
  */
10
10
  export declare class ViewFunctionExecutorV2<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends ViewFunctionExecutor<ChainApi> {
11
11
  chainHead: ChainHead;
12
- constructor(client: ISubstrateClientAt<ChainApi>, chainHead: ChainHead);
12
+ constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, chainHead: ChainHead);
13
13
  protected stateCall(callParams: StateCallParams): Promise<HexString>;
14
14
  }
@@ -19,17 +19,17 @@ interface SignedExtensionOptions {
19
19
  payloadOptions?: Partial<PayloadOptions>;
20
20
  }
21
21
  export declare abstract class SignedExtension<Data extends any = {}, AdditionalSigned extends any = []> implements ISignedExtension {
22
- readonly client: ISubstrateClient;
22
+ readonly client: ISubstrateClient<any, any>;
23
23
  readonly options?: SignedExtensionOptions | undefined;
24
24
  data: Data;
25
25
  additionalSigned: AdditionalSigned;
26
- constructor(client: ISubstrateClient, options?: SignedExtensionOptions | undefined);
26
+ constructor(client: ISubstrateClient<any, any>, options?: SignedExtensionOptions | undefined);
27
27
  init(): Promise<void>;
28
28
  fromPayload(payload: SignerPayloadJSON): Promise<void>;
29
29
  get identifier(): string;
30
30
  get $Data(): $.AnyShape;
31
31
  get $AdditionalSigned(): $.AnyShape;
32
- get registry(): PortableRegistry;
32
+ get registry(): PortableRegistry<any>;
33
33
  get signedExtensionDef(): {
34
34
  ident: string;
35
35
  typeId: number;
@@ -9,8 +9,8 @@ interface TxHooks {
9
9
  }
10
10
  export declare abstract class BaseSubmittableExtrinsic extends Extrinsic implements ISubmittableExtrinsic {
11
11
  #private;
12
- readonly client: ISubstrateClient;
13
- constructor(client: ISubstrateClient, call: IRuntimeTxCall);
12
+ readonly client: ISubstrateClient<any>;
13
+ constructor(client: ISubstrateClient<any>, call: IRuntimeTxCall);
14
14
  withHooks(hooks: TxHooks): void;
15
15
  protected transformTxResult<R extends ISubmittableResult = ISubmittableResult>(result: ISubmittableResult): R;
16
16
  paymentInfo(account: AddressOrPair, options?: Partial<PayloadOptions>): Promise<TxPaymentInfo>;
@@ -7,8 +7,8 @@ import { BaseSubmittableExtrinsic } from './BaseSubmittableExtrinsic.js';
7
7
  */
8
8
  export declare class SubmittableExtrinsicV2 extends BaseSubmittableExtrinsic {
9
9
  #private;
10
- client: DedotClient;
11
- constructor(client: DedotClient, call: IRuntimeTxCall);
10
+ client: DedotClient<any>;
11
+ constructor(client: DedotClient<any>, call: IRuntimeTxCall);
12
12
  send(): TxHash;
13
13
  send(callback: Callback): TxUnsub;
14
14
  }
@@ -0,0 +1,134 @@
1
+ import { BlockHash, Option } from '@dedot/codecs';
2
+ import { ArchiveStorageResult, PaginatedStorageQuery } from '@dedot/types/json-rpc';
3
+ import { HexString } from '@dedot/utils';
4
+ import { IJsonRpcClient } from '../../types.js';
5
+ import { JsonRpcGroup, JsonRpcGroupOptions } from './JsonRpcGroup.js';
6
+ /**
7
+ * @name Archive
8
+ * Archive JSON-RPC methods for accessing historical blockchain data.
9
+ * Functions with the `archive` prefix allow obtaining the state of the chain
10
+ * at any point in the present or in the past.
11
+ *
12
+ * JSON-RPC V2: https://paritytech.github.io/json-rpc-interface-spec/api/archive.html
13
+ */
14
+ export declare class Archive extends JsonRpcGroup {
15
+ #private;
16
+ constructor(client: IJsonRpcClient, options?: Partial<JsonRpcGroupOptions>);
17
+ /**
18
+ * Retrieves the body (list of transactions) of a given block.
19
+ * Returns an array of strings containing the hexadecimal-encoded SCALE-codec-encoded
20
+ * transactions in that block. If no block with that hash is found, null.
21
+ *
22
+ * @param hash - The block hash (optional, defaults to current finalized block)
23
+ * @returns Array of transaction hashes or null if block not found
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Get transactions from current finalized block
28
+ * const transactions = await archive.body();
29
+ *
30
+ * // Get transactions from specific block
31
+ * const transactions = await archive.body('0x1234...');
32
+ * ```
33
+ */
34
+ body(hash?: BlockHash): Promise<Option<Array<HexString>>>;
35
+ /**
36
+ * Get the chain's genesis hash.
37
+ * Returns a string containing the hexadecimal-encoded hash of the genesis block of the chain.
38
+ * This value is cached after the first call.
39
+ *
40
+ * @returns The genesis block hash
41
+ */
42
+ genesisHash(): Promise<HexString>;
43
+ /**
44
+ * Get the block's header.
45
+ * Returns a string containing the hexadecimal-encoded SCALE-codec encoding header of the block.
46
+ *
47
+ * @param hash - The block hash (optional, defaults to current finalized block)
48
+ * @returns The encoded block header or null if block not found
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * // Get header of current finalized block
53
+ * const header = await archive.header();
54
+ *
55
+ * // Get header of specific block
56
+ * const header = await archive.header('0x1234...');
57
+ * ```
58
+ */
59
+ header(hash?: BlockHash): Promise<Option<HexString>>;
60
+ /**
61
+ * Get the height of the current finalized block.
62
+ * Returns an integer height of the current finalized block of the chain.
63
+ *
64
+ * @returns The height of the finalized block
65
+ */
66
+ finalizedHeight(): Promise<number>;
67
+ /**
68
+ * Get the hash of the current finalized block.
69
+ * Returns a string containing the hexadecimal-encoded hash of the current finalized block.
70
+ * This is a convenience method that combines finalizedHeight() and hashByHeight().
71
+ *
72
+ * @returns The hash of the current finalized block
73
+ */
74
+ finalizedHash(): Promise<HexString>;
75
+ /**
76
+ * Get the hashes of blocks from the given height.
77
+ * Returns an array (possibly empty) of strings containing hexadecimal-encoded hashes of block headers.
78
+ *
79
+ * Note: For heights <= finalized height, there is guaranteed to be one block.
80
+ * For heights > finalized height, there may be zero, one or multiple blocks depending on forks.
81
+ *
82
+ * @param height - The block height
83
+ * @returns Array of block hashes at the given height
84
+ */
85
+ hashByHeight(height: number): Promise<Array<HexString>>;
86
+ /**
87
+ * Call into the Runtime API at a specified block's state.
88
+ *
89
+ * @param func - The runtime API function to call
90
+ * @param params - The parameters for the function call (SCALE-encoded)
91
+ * @param hash - The block hash (optional, defaults to current finalized block)
92
+ * @returns The result of the runtime call
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * // Call Core_version on current finalized block
97
+ * const version = await archive.call('Core_version', '0x');
98
+ *
99
+ * // Call Core_version on specific block
100
+ * const version = await archive.call('Core_version', '0x', '0x1234...');
101
+ * ```
102
+ */
103
+ call(func: string, params: HexString, hash?: BlockHash): Promise<HexString>;
104
+ /**
105
+ * Returns storage entries at a specific block's state.
106
+ * This method collects all storage events and returns them as a single result.
107
+ *
108
+ * @param items - Array of storage queries with optional pagination
109
+ * @param childTrie - Optional child trie key
110
+ * @param hash - The block hash (optional, defaults to current finalized block)
111
+ * @returns Storage results array
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * // Query storage from current finalized block
116
+ * const results = await archive.storage([{ key: '0x1234', type: 'value' }]);
117
+ *
118
+ * // Query storage from specific block
119
+ * const results = await archive.storage([{ key: '0x1234', type: 'value' }], null, '0xabcd...');
120
+ * ```
121
+ */
122
+ storage(items: Array<PaginatedStorageQuery>, childTrie?: HexString | null, hash?: BlockHash): Promise<ArchiveStorageResult>;
123
+ /**
124
+ * Clears the internal cache used for storing archive query results.
125
+ * This can be useful for memory management or when you want to force fresh data retrieval.
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * // Clear all cached results
130
+ * archive.clearCache();
131
+ * ```
132
+ */
133
+ clearCache(): void;
134
+ }
@@ -0,0 +1,228 @@
1
+ import { DedotError, LRUCache } from '@dedot/utils';
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
5
+ /**
6
+ * @name Archive
7
+ * Archive JSON-RPC methods for accessing historical blockchain data.
8
+ * Functions with the `archive` prefix allow obtaining the state of the chain
9
+ * at any point in the present or in the past.
10
+ *
11
+ * JSON-RPC V2: https://paritytech.github.io/json-rpc-interface-spec/api/archive.html
12
+ */
13
+ export class Archive extends JsonRpcGroup {
14
+ #genesisHash;
15
+ #cache;
16
+ constructor(client, options) {
17
+ super(client, { prefix: 'archive', supportedVersions: ['unstable', 'v1'], ...options });
18
+ this.#cache = new LRUCache(ARCHIVE_CACHE_CAPACITY, ARCHIVE_CACHE_TTL);
19
+ }
20
+ /**
21
+ * Retrieves the body (list of transactions) of a given block.
22
+ * Returns an array of strings containing the hexadecimal-encoded SCALE-codec-encoded
23
+ * transactions in that block. If no block with that hash is found, null.
24
+ *
25
+ * @param hash - The block hash (optional, defaults to current finalized block)
26
+ * @returns Array of transaction hashes or null if block not found
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // Get transactions from current finalized block
31
+ * const transactions = await archive.body();
32
+ *
33
+ * // Get transactions from specific block
34
+ * const transactions = await archive.body('0x1234...');
35
+ * ```
36
+ */
37
+ async body(hash) {
38
+ const blockHash = hash || (await this.finalizedHash());
39
+ const cacheKey = `${blockHash}::body`;
40
+ const cached = this.#cache.get(cacheKey);
41
+ if (cached !== null) {
42
+ return cached;
43
+ }
44
+ const result = await this.send('body', blockHash);
45
+ this.#cache.set(cacheKey, result);
46
+ return result;
47
+ }
48
+ /**
49
+ * Get the chain's genesis hash.
50
+ * Returns a string containing the hexadecimal-encoded hash of the genesis block of the chain.
51
+ * This value is cached after the first call.
52
+ *
53
+ * @returns The genesis block hash
54
+ */
55
+ async genesisHash() {
56
+ if (!this.#genesisHash) {
57
+ this.#genesisHash = await this.send('genesisHash');
58
+ }
59
+ return this.#genesisHash;
60
+ }
61
+ /**
62
+ * Get the block's header.
63
+ * Returns a string containing the hexadecimal-encoded SCALE-codec encoding header of the block.
64
+ *
65
+ * @param hash - The block hash (optional, defaults to current finalized block)
66
+ * @returns The encoded block header or null if block not found
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * // Get header of current finalized block
71
+ * const header = await archive.header();
72
+ *
73
+ * // Get header of specific block
74
+ * const header = await archive.header('0x1234...');
75
+ * ```
76
+ */
77
+ async header(hash) {
78
+ const blockHash = hash || (await this.finalizedHash());
79
+ const cacheKey = `${blockHash}::header`;
80
+ const cached = this.#cache.get(cacheKey);
81
+ if (cached !== null) {
82
+ return cached;
83
+ }
84
+ const result = await this.send('header', blockHash);
85
+ this.#cache.set(cacheKey, result);
86
+ return result;
87
+ }
88
+ /**
89
+ * Get the height of the current finalized block.
90
+ * Returns an integer height of the current finalized block of the chain.
91
+ *
92
+ * @returns The height of the finalized block
93
+ */
94
+ async finalizedHeight() {
95
+ return this.send('finalizedHeight');
96
+ }
97
+ /**
98
+ * Get the hash of the current finalized block.
99
+ * Returns a string containing the hexadecimal-encoded hash of the current finalized block.
100
+ * This is a convenience method that combines finalizedHeight() and hashByHeight().
101
+ *
102
+ * @returns The hash of the current finalized block
103
+ */
104
+ async finalizedHash() {
105
+ const height = await this.finalizedHeight();
106
+ const hashes = await this.hashByHeight(height);
107
+ if (hashes.length === 0) {
108
+ throw new Error(`No block found at finalized height ${height}`);
109
+ }
110
+ return hashes[0];
111
+ }
112
+ /**
113
+ * Get the hashes of blocks from the given height.
114
+ * Returns an array (possibly empty) of strings containing hexadecimal-encoded hashes of block headers.
115
+ *
116
+ * Note: For heights <= finalized height, there is guaranteed to be one block.
117
+ * For heights > finalized height, there may be zero, one or multiple blocks depending on forks.
118
+ *
119
+ * @param height - The block height
120
+ * @returns Array of block hashes at the given height
121
+ */
122
+ async hashByHeight(height) {
123
+ return this.send('hashByHeight', height);
124
+ }
125
+ /**
126
+ * Call into the Runtime API at a specified block's state.
127
+ *
128
+ * @param func - The runtime API function to call
129
+ * @param params - The parameters for the function call (SCALE-encoded)
130
+ * @param hash - The block hash (optional, defaults to current finalized block)
131
+ * @returns The result of the runtime call
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // Call Core_version on current finalized block
136
+ * const version = await archive.call('Core_version', '0x');
137
+ *
138
+ * // Call Core_version on specific block
139
+ * const version = await archive.call('Core_version', '0x', '0x1234...');
140
+ * ```
141
+ */
142
+ async call(func, params, hash) {
143
+ const blockHash = hash || (await this.finalizedHash());
144
+ const cacheKey = `${blockHash}::call::${func}::${params}`;
145
+ const cached = this.#cache.get(cacheKey);
146
+ if (cached !== null) {
147
+ return cached;
148
+ }
149
+ const result = await this.send('call', blockHash, func, params);
150
+ if (!result.success) {
151
+ throw new DedotError(result.error);
152
+ }
153
+ this.#cache.set(cacheKey, result.value);
154
+ return result.value;
155
+ }
156
+ /**
157
+ * Returns storage entries at a specific block's state via subscription.
158
+ *
159
+ * @param items - Array of storage queries with optional pagination
160
+ * @param childTrie - Optional child trie key
161
+ * @param callback - Callback to receive storage events
162
+ * @param hash - The block hash (optional, defaults to current finalized block)
163
+ * @returns Unsubscribe function
164
+ */
165
+ async #storageSubscription(items, childTrie, callback, hash) {
166
+ const blockHash = hash || (await this.finalizedHash());
167
+ return this.send('storage', blockHash, items, childTrie, callback);
168
+ }
169
+ /**
170
+ * Returns storage entries at a specific block's state.
171
+ * This method collects all storage events and returns them as a single result.
172
+ *
173
+ * @param items - Array of storage queries with optional pagination
174
+ * @param childTrie - Optional child trie key
175
+ * @param hash - The block hash (optional, defaults to current finalized block)
176
+ * @returns Storage results array
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * // Query storage from current finalized block
181
+ * const results = await archive.storage([{ key: '0x1234', type: 'value' }]);
182
+ *
183
+ * // Query storage from specific block
184
+ * const results = await archive.storage([{ key: '0x1234', type: 'value' }], null, '0xabcd...');
185
+ * ```
186
+ */
187
+ async storage(items, childTrie, hash) {
188
+ return new Promise(async (resolve, reject) => {
189
+ const results = [];
190
+ const blockHash = hash || (await this.finalizedHash());
191
+ // Generate cache key
192
+ const cacheKey = `${blockHash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
193
+ // Check cache
194
+ const cached = this.#cache.get(cacheKey);
195
+ if (cached !== null) {
196
+ return resolve(cached);
197
+ }
198
+ this.#storageSubscription(items, childTrie || null, (event) => {
199
+ switch (event.event) {
200
+ case 'storage':
201
+ results.push(event);
202
+ break;
203
+ case 'storageDone':
204
+ // Set cache after successful completion
205
+ this.#cache.set(cacheKey, results);
206
+ resolve(results);
207
+ break;
208
+ case 'storageError':
209
+ reject(new DedotError(event.error));
210
+ break;
211
+ }
212
+ }, blockHash).catch(reject);
213
+ });
214
+ }
215
+ /**
216
+ * Clears the internal cache used for storing archive query results.
217
+ * This can be useful for memory management or when you want to force fresh data retrieval.
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * // Clear all cached results
222
+ * archive.clearCache();
223
+ * ```
224
+ */
225
+ clearCache() {
226
+ this.#cache.clear();
227
+ }
228
+ }
@@ -2,6 +2,7 @@ import { BlockHash, Option } from '@dedot/codecs';
2
2
  import type { ChainHeadRuntimeVersion, OperationId, StorageQuery, StorageResult } from '@dedot/types/json-rpc';
3
3
  import { Deferred, HexString } from '@dedot/utils';
4
4
  import type { IJsonRpcClient } from '../../../types.js';
5
+ import { Archive } from '../Archive.js';
5
6
  import { JsonRpcGroup, type JsonRpcGroupOptions } from '../JsonRpcGroup.js';
6
7
  export type OperationHandler<T = any> = {
7
8
  operationId: OperationId;
@@ -20,6 +21,15 @@ export declare const MIN_FINALIZED_QUEUE_SIZE = 10;
20
21
  export declare class ChainHead extends JsonRpcGroup<ChainHeadEvent> {
21
22
  #private;
22
23
  constructor(client: IJsonRpcClient, options?: Partial<JsonRpcGroupOptions>);
24
+ /**
25
+ * Attach an Archive instance as fallback for operations that fail due to unpinned blocks.
26
+ * When a ChainHeadBlockNotPinnedError occurs, the operation will automatically fallback
27
+ * to the Archive API to attempt to retrieve the data from historical blocks.
28
+ *
29
+ * @param archive - Archive instance to use as fallback
30
+ * @returns this ChainHead instance for method chaining
31
+ */
32
+ withArchive(archive: Archive): this;
23
33
  runtimeVersion(): Promise<ChainHeadRuntimeVersion>;
24
34
  bestRuntimeVersion(): Promise<ChainHeadRuntimeVersion>;
25
35
  finalizedHash(): Promise<BlockHash>;
@@ -67,4 +77,15 @@ export declare class ChainHead extends JsonRpcGroup<ChainHeadEvent> {
67
77
  * @protected
68
78
  */
69
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;
70
91
  }