@dedot/api 0.8.1 → 0.9.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 (53) hide show
  1. package/cjs/client/BaseSubstrateClient.js +25 -1
  2. package/cjs/client/DedotClient.js +4 -0
  3. package/cjs/client/LegacyClient.js +4 -0
  4. package/cjs/executor/StorageQueryExecutor.js +8 -15
  5. package/cjs/executor/v2/StorageQueryExecutorV2.js +7 -33
  6. package/cjs/extrinsic/extensions/ExtraSignedExtension.js +24 -5
  7. package/cjs/extrinsic/extensions/FallbackSignedExtension.js +63 -0
  8. package/cjs/extrinsic/extensions/index.js +1 -0
  9. package/cjs/extrinsic/extensions/known/index.js +11 -8
  10. package/cjs/storage/BaseStorageQuery.js +27 -0
  11. package/cjs/storage/LegacyStorageQuery.js +69 -0
  12. package/cjs/storage/NewStorageQuery.js +85 -0
  13. package/cjs/storage/index.js +3 -0
  14. package/client/BaseSubstrateClient.d.ts +34 -1
  15. package/client/BaseSubstrateClient.js +25 -1
  16. package/client/DedotClient.d.ts +3 -1
  17. package/client/DedotClient.js +4 -0
  18. package/client/LegacyClient.d.ts +3 -1
  19. package/client/LegacyClient.js +4 -0
  20. package/executor/StorageQueryExecutor.d.ts +3 -2
  21. package/executor/StorageQueryExecutor.js +7 -14
  22. package/executor/v2/StorageQueryExecutorV2.d.ts +4 -6
  23. package/executor/v2/StorageQueryExecutorV2.js +7 -33
  24. package/extrinsic/extensions/ExtraSignedExtension.d.ts +6 -0
  25. package/extrinsic/extensions/ExtraSignedExtension.js +25 -6
  26. package/extrinsic/extensions/FallbackSignedExtension.d.ts +33 -0
  27. package/extrinsic/extensions/FallbackSignedExtension.js +58 -0
  28. package/extrinsic/extensions/index.d.ts +1 -0
  29. package/extrinsic/extensions/index.js +1 -0
  30. package/extrinsic/extensions/known/index.d.ts +11 -0
  31. package/extrinsic/extensions/known/index.js +11 -8
  32. package/package.json +10 -10
  33. package/storage/BaseStorageQuery.d.ts +41 -0
  34. package/storage/BaseStorageQuery.js +23 -0
  35. package/storage/LegacyStorageQuery.d.ts +35 -0
  36. package/storage/LegacyStorageQuery.js +65 -0
  37. package/storage/NewStorageQuery.d.ts +35 -0
  38. package/storage/NewStorageQuery.js +81 -0
  39. package/storage/index.d.ts +3 -0
  40. package/storage/index.js +3 -0
  41. package/types.d.ts +17 -1
  42. package/cjs/extrinsic/extensions/known/CheckNonZeroSender.js +0 -10
  43. package/cjs/extrinsic/extensions/known/CheckWeight.js +0 -10
  44. package/cjs/extrinsic/extensions/known/PrevalidateAttests.js +0 -11
  45. package/cjs/extrinsic/extensions/known/StorageWeightReclaim.js +0 -10
  46. package/extrinsic/extensions/known/CheckNonZeroSender.d.ts +0 -6
  47. package/extrinsic/extensions/known/CheckNonZeroSender.js +0 -6
  48. package/extrinsic/extensions/known/CheckWeight.d.ts +0 -6
  49. package/extrinsic/extensions/known/CheckWeight.js +0 -6
  50. package/extrinsic/extensions/known/PrevalidateAttests.d.ts +0 -7
  51. package/extrinsic/extensions/known/PrevalidateAttests.js +0 -7
  52. package/extrinsic/extensions/known/StorageWeightReclaim.d.ts +0 -6
  53. package/extrinsic/extensions/known/StorageWeightReclaim.js +0 -6
@@ -1,7 +1,8 @@
1
1
  import { BlockHash } from '@dedot/codecs';
2
2
  import type { JsonRpcProvider } from '@dedot/providers';
3
- import { GenericSubstrateApi, RpcLegacy, VersionedGenericSubstrateApi } from '@dedot/types';
3
+ import { GenericSubstrateApi, RpcLegacy, RpcVersion, VersionedGenericSubstrateApi } from '@dedot/types';
4
4
  import type { SubstrateApi } from '../chaintypes/index.js';
5
+ import { BaseStorageQuery } from '../storage/index.js';
5
6
  import type { ApiOptions, ISubstrateClientAt } from '../types.js';
6
7
  import { BaseSubstrateClient } from './BaseSubstrateClient.js';
7
8
  /**
@@ -129,4 +130,5 @@ export declare class LegacyClient<ChainApi extends VersionedGenericSubstrateApi
129
130
  * @param hash
130
131
  */
131
132
  at<ChainApiAt extends GenericSubstrateApi = ChainApi[RpcLegacy]>(hash: BlockHash): Promise<ISubstrateClientAt<ChainApiAt>>;
133
+ protected getStorageQuery(): BaseStorageQuery<RpcVersion>;
132
134
  }
@@ -1,6 +1,7 @@
1
1
  import { PortableRegistry } from '@dedot/codecs';
2
2
  import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutor, StorageQueryExecutor, TxExecutor, } from '../executor/index.js';
3
3
  import { newProxyChain } from '../proxychain.js';
4
+ import { LegacyStorageQuery } from '../storage/index.js';
4
5
  import { BaseSubstrateClient } from './BaseSubstrateClient.js';
5
6
  const KEEP_ALIVE_INTERVAL = 10_000; // in ms
6
7
  /**
@@ -247,4 +248,7 @@ export class LegacyClient// prettier-end-here
247
248
  this.#apiAtCache[hash] = api;
248
249
  return api;
249
250
  }
251
+ getStorageQuery() {
252
+ return new LegacyStorageQuery(this);
253
+ }
250
254
  }
@@ -1,6 +1,6 @@
1
1
  import { BlockHash, Option, StorageData, StorageKey } from '@dedot/codecs';
2
- import type { AsyncMethod, Callback, GenericStorageQuery, GenericSubstrateApi, Unsub } from '@dedot/types';
3
- import { QueryableStorage } from '../storage/QueryableStorage.js';
2
+ import type { AsyncMethod, Callback, GenericStorageQuery, GenericSubstrateApi, RpcVersion, Unsub } from '@dedot/types';
3
+ import { type BaseStorageQuery, QueryableStorage } from '../storage/index.js';
4
4
  import { Executor } from './Executor.js';
5
5
  /**
6
6
  * @name StorageQueryExecutor
@@ -9,6 +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
13
  protected queryStorage(keys: StorageKey[], hash?: BlockHash): Promise<Record<StorageKey, Option<StorageData>>>;
13
14
  protected subscribeStorage(keys: StorageKey[], callback: Callback<Array<StorageData | undefined>>): Promise<Unsub>;
14
15
  }
@@ -1,5 +1,5 @@
1
1
  import { assert, isFunction, isObject } from '@dedot/utils';
2
- import { QueryableStorage } from '../storage/QueryableStorage.js';
2
+ import { LegacyStorageQuery, QueryableStorage } from '../storage/index.js';
3
3
  import { Executor } from './Executor.js';
4
4
  const DEFAULT_KEYS_PAGE_SIZE = 1000;
5
5
  const DEFAULT_ENTRIES_PAGE_SIZE = 250;
@@ -98,22 +98,15 @@ export class StorageQueryExecutor extends Executor {
98
98
  };
99
99
  return { pagedKeys, pagedEntries };
100
100
  }
101
+ getStorageQuery() {
102
+ return new LegacyStorageQuery(this.client);
103
+ }
101
104
  async queryStorage(keys, hash) {
102
- const changeSets = await this.client.rpc.state_queryStorageAt(keys, hash);
103
- return changeSets[0].changes.reduce((o, [key, value]) => {
104
- o[key] = value ?? undefined;
105
- return o;
106
- }, {});
105
+ return this.getStorageQuery().query(keys, hash);
107
106
  }
108
107
  subscribeStorage(keys, callback) {
109
- const lastChanges = {};
110
- return this.client.rpc.state_subscribeStorage(keys, (changeSet) => {
111
- changeSet.changes.forEach(([key, value]) => {
112
- if (lastChanges[key] !== value) {
113
- lastChanges[key] = value ?? undefined;
114
- }
115
- });
116
- return callback(keys.map((key) => lastChanges[key]));
108
+ return this.getStorageQuery().subscribe(keys, (results) => {
109
+ callback(keys.map((key) => results[key]));
117
110
  });
118
111
  }
119
112
  }
@@ -1,8 +1,7 @@
1
- import { BlockHash, Option, StorageData, StorageKey } from '@dedot/codecs';
2
- import type { AsyncMethod, Callback, GenericSubstrateApi } from '@dedot/types';
3
- import { HexString } from '@dedot/utils';
1
+ import { BlockHash } from '@dedot/codecs';
2
+ import type { AsyncMethod, GenericSubstrateApi, RpcVersion } from '@dedot/types';
4
3
  import { ChainHead } from '../../json-rpc/index.js';
5
- import { QueryableStorage } from '../../storage/QueryableStorage.js';
4
+ import { type BaseStorageQuery, QueryableStorage } from '../../storage/index.js';
6
5
  import { ISubstrateClientAt } from '../../types.js';
7
6
  import { StorageQueryExecutor } from '../StorageQueryExecutor.js';
8
7
  /**
@@ -12,6 +11,5 @@ export declare class StorageQueryExecutorV2<ChainApi extends GenericSubstrateApi
12
11
  chainHead: ChainHead;
13
12
  constructor(client: ISubstrateClientAt<ChainApi>, chainHead: ChainHead, atBlockHash?: BlockHash);
14
13
  protected exposeStorageMapMethods(entry: QueryableStorage): Record<string, AsyncMethod>;
15
- protected queryStorage(keys: StorageKey[], at?: BlockHash): Promise<Record<StorageKey, Option<StorageData>>>;
16
- protected subscribeStorage(keys: HexString[], callback: Callback<Array<StorageData | undefined>>): Promise<() => Promise<void>>;
14
+ protected getStorageQuery(): BaseStorageQuery<RpcVersion>;
17
15
  }
@@ -1,4 +1,5 @@
1
1
  import { assert } from '@dedot/utils';
2
+ import { NewStorageQuery } from '../../storage/index.js';
2
3
  import { StorageQueryExecutor } from '../StorageQueryExecutor.js';
3
4
  /**
4
5
  * @name StorageQueryExecutorV2
@@ -28,38 +29,11 @@ export class StorageQueryExecutorV2 extends StorageQueryExecutor {
28
29
  };
29
30
  return { entries };
30
31
  }
31
- async queryStorage(keys, at) {
32
- const results = await this.chainHead.storage(keys.map((key) => ({ type: 'value', key })), undefined, at);
33
- return results.reduce((o, r) => {
34
- o[r.key] = (r.value ?? undefined);
35
- return o;
36
- }, {});
37
- }
38
- async subscribeStorage(keys, callback) {
39
- let best = await this.chainHead.bestBlock();
40
- let eventToListen = 'bestBlock';
41
- // TODO subscribe to finalized data source
42
- // initialHash = this.chainHead.finalizedHash;
43
- // eventToListen = 'finalizedBlock';
44
- const latestChanges = new Map();
45
- const pull = async ({ hash }) => {
46
- const results = await this.queryStorage(keys, hash);
47
- let changed = false;
48
- keys.forEach((key) => {
49
- const newValue = results[key];
50
- if (latestChanges.size > 0 && latestChanges.get(key) === newValue)
51
- return;
52
- changed = true;
53
- latestChanges.set(key, newValue);
54
- });
55
- if (!changed)
56
- return;
57
- callback(keys.map((key) => latestChanges.get(key)));
58
- };
59
- await pull(best);
60
- const unsub = this.chainHead.on(eventToListen, pull);
61
- return async () => {
62
- unsub();
63
- };
32
+ getStorageQuery() {
33
+ // @ts-ignore little trick to make querying data client.at instance works here,
34
+ // TODO need to rethink about this
35
+ if (!this.client['chainHead'])
36
+ this.client['chainHead'] = this.chainHead;
37
+ return new NewStorageQuery(this.client);
64
38
  }
65
39
  }
@@ -9,6 +9,12 @@ export declare class ExtraSignedExtension extends SignedExtension<any[], any[]>
9
9
  get $Data(): $.AnyShape;
10
10
  get $AdditionalSigned(): $.AnyShape;
11
11
  get $Payload(): $.AnyShape;
12
+ /**
13
+ * Check if the extension requires no external inputs (e.g: struct or tuple with empty types like `()` or `[]`)
14
+ * @param extDef - The definition of the signed extension
15
+ * @returns boolean
16
+ */
17
+ private isRequireNoExternalInputs;
12
18
  toPayload(call?: HexString): SignerPayloadJSON;
13
19
  toRawPayload(call?: HexString): SignerPayloadRaw;
14
20
  }
@@ -1,6 +1,7 @@
1
1
  import * as $ from '@dedot/shape';
2
- import { assert, ensurePresence, u8aToHex } from '@dedot/utils';
2
+ import { ensurePresence, u8aToHex } from '@dedot/utils';
3
3
  import { SignedExtension } from './SignedExtension.js';
4
+ import { FallbackSignedExtension, isEmptyStructOrTuple } from './FallbackSignedExtension.js';
4
5
  import { knownSignedExtensions } from './known/index.js';
5
6
  export class ExtraSignedExtension extends SignedExtension {
6
7
  #signedExtensions;
@@ -34,13 +35,31 @@ export class ExtraSignedExtension extends SignedExtension {
34
35
  const { signedExtensions: userSignedExtensions = {} } = this.client.options;
35
36
  const Extension = userSignedExtensions[extDef.ident] ||
36
37
  knownSignedExtensions[extDef.ident];
37
- assert(Extension, `SignedExtension for ${extDef.ident} not found`);
38
- return new Extension(this.client, {
39
- ...ensurePresence(this.options),
40
- def: extDef,
41
- });
38
+ if (Extension) {
39
+ return new Extension(this.client, {
40
+ ...ensurePresence(this.options),
41
+ def: extDef,
42
+ });
43
+ }
44
+ else if (this.isRequireNoExternalInputs(extDef)) {
45
+ return new FallbackSignedExtension(this.client, {
46
+ ...ensurePresence(this.options),
47
+ def: extDef,
48
+ }, extDef.ident);
49
+ }
50
+ // For extensions that require input but aren't implemented, throw an error
51
+ throw new Error(`SignedExtension for ${extDef.ident} requires input but is not implemented`);
42
52
  });
43
53
  }
54
+ /**
55
+ * Check if the extension requires no external inputs (e.g: struct or tuple with empty types like `()` or `[]`)
56
+ * @param extDef - The definition of the signed extension
57
+ * @returns boolean
58
+ */
59
+ isRequireNoExternalInputs(extDef) {
60
+ return (isEmptyStructOrTuple(this.registry, extDef.typeId) &&
61
+ isEmptyStructOrTuple(this.registry, extDef.additionalSigned));
62
+ }
44
63
  toPayload(call = '0x') {
45
64
  const signedExtensions = this.#signedExtensions.map((se) => se.identifier);
46
65
  const { version } = this.registry.metadata.extrinsic;
@@ -0,0 +1,33 @@
1
+ import { PortableRegistry } from '@dedot/codecs';
2
+ import { SignerPayloadJSON } from '@dedot/types';
3
+ import { SignedExtension } from './SignedExtension.js';
4
+ import { ISubstrateClient } from '../../types.js';
5
+ /**
6
+ * A fallback signed extension that can be used for extensions
7
+ * that don't require external input.
8
+ *
9
+ * This extension is automatically used for:
10
+ * - Unknown extensions with empty struct or tuple types
11
+ * - Known extensions that don't require input, such as:
12
+ * - CheckNonZeroSender: Ensures sender is not the zero address
13
+ * - CheckWeight: Block resource (weight) limit check
14
+ * - PrevalidateAttests: Validates `attest` calls prior to execution
15
+ * - StorageWeightReclaim: Storage weight reclaim mechanism
16
+ *
17
+ * These extensions have empty struct or tuple types and don't need explicit implementation.
18
+ */
19
+ export declare class FallbackSignedExtension extends SignedExtension {
20
+ private readonly extensionIdent;
21
+ constructor(client: ISubstrateClient, options: any, extensionIdent: string);
22
+ get identifier(): string;
23
+ init(): Promise<void>;
24
+ toPayload(): Partial<SignerPayloadJSON>;
25
+ }
26
+ /**
27
+ * Checks if a type is an empty struct or tuple (doesn't require external input).
28
+ *
29
+ * @param registry The portable registry
30
+ * @param typeId The type ID to check
31
+ * @returns True if the type is an empty struct or tuple, false otherwise
32
+ */
33
+ export declare function isEmptyStructOrTuple(registry: PortableRegistry, typeId: number): boolean;
@@ -0,0 +1,58 @@
1
+ import { SignedExtension } from './SignedExtension.js';
2
+ /**
3
+ * A fallback signed extension that can be used for extensions
4
+ * that don't require external input.
5
+ *
6
+ * This extension is automatically used for:
7
+ * - Unknown extensions with empty struct or tuple types
8
+ * - Known extensions that don't require input, such as:
9
+ * - CheckNonZeroSender: Ensures sender is not the zero address
10
+ * - CheckWeight: Block resource (weight) limit check
11
+ * - PrevalidateAttests: Validates `attest` calls prior to execution
12
+ * - StorageWeightReclaim: Storage weight reclaim mechanism
13
+ *
14
+ * These extensions have empty struct or tuple types and don't need explicit implementation.
15
+ */
16
+ export class FallbackSignedExtension extends SignedExtension {
17
+ extensionIdent;
18
+ constructor(client, options, extensionIdent) {
19
+ super(client, options);
20
+ this.extensionIdent = extensionIdent;
21
+ }
22
+ get identifier() {
23
+ return this.extensionIdent;
24
+ }
25
+ async init() {
26
+ // Initialize with empty data and additionalSigned
27
+ this.data = {};
28
+ this.additionalSigned = [];
29
+ }
30
+ toPayload() {
31
+ return {}; // No payload contribution
32
+ }
33
+ }
34
+ /**
35
+ * Checks if a type is an empty struct or tuple (doesn't require external input).
36
+ *
37
+ * @param registry The portable registry
38
+ * @param typeId The type ID to check
39
+ * @returns True if the type is an empty struct or tuple, false otherwise
40
+ */
41
+ export function isEmptyStructOrTuple(registry, typeId) {
42
+ try {
43
+ const type = registry.findType(typeId);
44
+ // Check if it's an empty struct
45
+ if (type.typeDef.type === 'Struct' && type.typeDef.value.fields.length === 0) {
46
+ return true;
47
+ }
48
+ // Check if it's an empty tuple
49
+ if (type.typeDef.type === 'Tuple' && type.typeDef.value.fields.length === 0) {
50
+ return true;
51
+ }
52
+ }
53
+ catch (error) {
54
+ // Ignore errors
55
+ }
56
+ // All other cases (including errors) require input
57
+ return false;
58
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './SignedExtension.js';
2
2
  export * from './known/index.js';
3
3
  export * from './ExtraSignedExtension.js';
4
+ export * from './FallbackSignedExtension.js';
@@ -1,3 +1,4 @@
1
1
  export * from './SignedExtension.js';
2
2
  export * from './known/index.js';
3
3
  export * from './ExtraSignedExtension.js';
4
+ export * from './FallbackSignedExtension.js';
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Known signed extensions that require external input.
3
+ *
4
+ * Note: Extensions that don't require external input are automatically handled by FallbackSignedExtension:
5
+ * - CheckNonZeroSender
6
+ * - CheckWeight
7
+ * - PrevalidateAttests
8
+ * - StorageWeightReclaim
9
+ *
10
+ * These extensions have empty struct or tuple types and don't need explicit implementation.
11
+ */
1
12
  import { ISignedExtension } from '../SignedExtension.js';
2
13
  export type AnySignedExtension = new (...args: any[]) => ISignedExtension;
3
14
  export declare const knownSignedExtensions: Record<string, AnySignedExtension>;
@@ -1,26 +1,29 @@
1
+ /**
2
+ * Known signed extensions that require external input.
3
+ *
4
+ * Note: Extensions that don't require external input are automatically handled by FallbackSignedExtension:
5
+ * - CheckNonZeroSender
6
+ * - CheckWeight
7
+ * - PrevalidateAttests
8
+ * - StorageWeightReclaim
9
+ *
10
+ * These extensions have empty struct or tuple types and don't need explicit implementation.
11
+ */
1
12
  import { ChargeAssetTxPayment } from './ChargeAssetTxPayment.js';
2
13
  import { ChargeTransactionPayment } from './ChargeTransactionPayment.js';
3
14
  import { CheckGenesis } from './CheckGenesis.js';
4
15
  import { CheckMetadataHash } from './CheckMetadataHash.js';
5
16
  import { CheckMortality } from './CheckMortality.js';
6
- import { CheckNonZeroSender } from './CheckNonZeroSender.js';
7
17
  import { CheckNonce } from './CheckNonce.js';
8
18
  import { CheckSpecVersion } from './CheckSpecVersion.js';
9
19
  import { CheckTxVersion } from './CheckTxVersion.js';
10
- import { CheckWeight } from './CheckWeight.js';
11
- import { PrevalidateAttests } from './PrevalidateAttests.js';
12
- import { StorageWeightReclaim } from './StorageWeightReclaim.js';
13
20
  export const knownSignedExtensions = {
14
- CheckNonZeroSender,
15
21
  CheckSpecVersion,
16
22
  CheckTxVersion,
17
23
  CheckGenesis,
18
24
  CheckMortality,
19
25
  CheckNonce,
20
- CheckWeight,
21
26
  ChargeTransactionPayment,
22
- PrevalidateAttests,
23
27
  ChargeAssetTxPayment,
24
28
  CheckMetadataHash,
25
- StorageWeightReclaim,
26
29
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dedot/api",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "A delightful JavaScript/TypeScript client for Polkadot & Substrate",
5
5
  "author": "Thang X. Vu <thang@coongcrafts.io>",
6
6
  "homepage": "https://github.com/dedotdev/dedot/tree/main/packages/api",
@@ -13,18 +13,18 @@
13
13
  "type": "module",
14
14
  "sideEffects": false,
15
15
  "dependencies": {
16
- "@dedot/codecs": "0.8.1",
17
- "@dedot/providers": "0.8.1",
18
- "@dedot/runtime-specs": "0.8.1",
19
- "@dedot/shape": "0.8.1",
20
- "@dedot/storage": "0.8.1",
21
- "@dedot/types": "0.8.1",
22
- "@dedot/utils": "0.8.1"
16
+ "@dedot/codecs": "0.9.0",
17
+ "@dedot/providers": "0.9.0",
18
+ "@dedot/runtime-specs": "0.9.0",
19
+ "@dedot/shape": "0.9.0",
20
+ "@dedot/storage": "0.9.0",
21
+ "@dedot/types": "0.9.0",
22
+ "@dedot/utils": "0.9.0"
23
23
  },
24
24
  "scripts": {
25
25
  "build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json",
26
26
  "clean": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo ./tsconfig.build.tsbuildinfo",
27
- "test": "vitest --watch=false"
27
+ "test": "npx vitest --watch=false"
28
28
  },
29
29
  "exports": {
30
30
  ".": {
@@ -48,7 +48,7 @@
48
48
  "node": ">=18"
49
49
  },
50
50
  "license": "Apache-2.0",
51
- "gitHead": "03ad1a7c81a90b78b39206b30fc96a65537faaee",
51
+ "gitHead": "cebae55a3525e2c9bcdd70322027855a594a287d",
52
52
  "module": "./index.js",
53
53
  "types": "./index.d.ts"
54
54
  }
@@ -0,0 +1,41 @@
1
+ import { BlockHash, StorageData, StorageKey } from '@dedot/codecs';
2
+ import type { Callback, RpcVersion, Unsub, VersionedGenericSubstrateApi } from '@dedot/types';
3
+ import type { SubstrateApi } from '../chaintypes/index.js';
4
+ import { BaseSubstrateClient } from '../client/BaseSubstrateClient.js';
5
+ /**
6
+ * @name BaseStorageQuery
7
+ * @description
8
+ * An abstract service that provides functionality for querying and subscribing to
9
+ * multiple storage items. This service is designed to be extended by version-specific
10
+ * implementations that handle the details of interacting with different JSON-RPC APIs.
11
+ *
12
+ * The service provides a simple interface for:
13
+ * - Querying multiple storage keys in a single call
14
+ * - Subscribing to changes in multiple storage keys
15
+ *
16
+ * This abstraction eliminates code duplication between different client implementations
17
+ * and provides a consistent interface for storage operations.
18
+ */
19
+ export declare abstract class BaseStorageQuery<Rv extends RpcVersion, ChainApi extends VersionedGenericSubstrateApi = SubstrateApi, T extends BaseSubstrateClient<Rv, ChainApi> = BaseSubstrateClient<Rv, ChainApi>> {
20
+ protected client: T;
21
+ /**
22
+ * @param client - The substrate client instance
23
+ */
24
+ constructor(client: T);
25
+ /**
26
+ * Query multiple storage items in a single call
27
+ *
28
+ * @param keys - Array of storage keys to query
29
+ * @param at - Optional block hash to query at
30
+ * @returns Promise resolving to a record mapping storage keys to their values
31
+ */
32
+ abstract query(keys: StorageKey[], at?: BlockHash): Promise<Record<StorageKey, StorageData | undefined>>;
33
+ /**
34
+ * Subscribe to multiple storage items
35
+ *
36
+ * @param keys - Array of storage keys to subscribe to
37
+ * @param callback - Function to call when storage values change
38
+ * @returns Promise resolving to an unsubscribe function
39
+ */
40
+ abstract subscribe(keys: StorageKey[], callback: Callback<Record<StorageKey, StorageData | undefined>>): Promise<Unsub>;
41
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @name BaseStorageQuery
3
+ * @description
4
+ * An abstract service that provides functionality for querying and subscribing to
5
+ * multiple storage items. This service is designed to be extended by version-specific
6
+ * implementations that handle the details of interacting with different JSON-RPC APIs.
7
+ *
8
+ * The service provides a simple interface for:
9
+ * - Querying multiple storage keys in a single call
10
+ * - Subscribing to changes in multiple storage keys
11
+ *
12
+ * This abstraction eliminates code duplication between different client implementations
13
+ * and provides a consistent interface for storage operations.
14
+ */
15
+ export class BaseStorageQuery {
16
+ client;
17
+ /**
18
+ * @param client - The substrate client instance
19
+ */
20
+ constructor(client) {
21
+ this.client = client;
22
+ }
23
+ }
@@ -0,0 +1,35 @@
1
+ import { BlockHash, StorageData, StorageKey } from '@dedot/codecs';
2
+ import type { Callback, RpcLegacy, Unsub, VersionedGenericSubstrateApi } from '@dedot/types';
3
+ import type { SubstrateApi } from '../chaintypes/index.js';
4
+ import { LegacyClient } from '../client/LegacyClient.js';
5
+ import { BaseStorageQuery } from './BaseStorageQuery.js';
6
+ /**
7
+ * @name LegacyStorageQuery
8
+ * @description
9
+ * Implementation of BaseStorageQuery for the legacy JSON-RPC API (v1).
10
+ * This service handles storage queries using the state_queryStorageAt and
11
+ * state_subscribeStorage RPC methods.
12
+ *
13
+ * It provides:
14
+ * - One-time queries using state_queryStorageAt
15
+ * - Subscriptions using state_subscribeStorage
16
+ * - Efficient change tracking for subscriptions
17
+ */
18
+ export declare class LegacyStorageQuery<ChainApi extends VersionedGenericSubstrateApi = SubstrateApi> extends BaseStorageQuery<RpcLegacy, ChainApi, LegacyClient<ChainApi>> {
19
+ /**
20
+ * Query multiple storage items in a single call using state_queryStorageAt
21
+ *
22
+ * @param keys - Array of storage keys to query
23
+ * @param at - Optional block hash to query at (defaults to current/best block)
24
+ * @returns Promise resolving to a record mapping storage keys to their values
25
+ */
26
+ query(keys: StorageKey[], at?: BlockHash): Promise<Record<StorageKey, StorageData | undefined>>;
27
+ /**
28
+ * Subscribe to multiple storage items using state_subscribeStorage
29
+ *
30
+ * @param keys - Array of storage keys to subscribe to
31
+ * @param callback - Function to call when storage values change
32
+ * @returns Promise resolving to an unsubscribe function
33
+ */
34
+ subscribe(keys: StorageKey[], callback: Callback<Record<StorageKey, StorageData | undefined>>): Promise<Unsub>;
35
+ }
@@ -0,0 +1,65 @@
1
+ import { BaseStorageQuery } from './BaseStorageQuery.js';
2
+ /**
3
+ * @name LegacyStorageQuery
4
+ * @description
5
+ * Implementation of BaseStorageQuery for the legacy JSON-RPC API (v1).
6
+ * This service handles storage queries using the state_queryStorageAt and
7
+ * state_subscribeStorage RPC methods.
8
+ *
9
+ * It provides:
10
+ * - One-time queries using state_queryStorageAt
11
+ * - Subscriptions using state_subscribeStorage
12
+ * - Efficient change tracking for subscriptions
13
+ */
14
+ export class LegacyStorageQuery extends BaseStorageQuery {
15
+ /**
16
+ * Query multiple storage items in a single call using state_queryStorageAt
17
+ *
18
+ * @param keys - Array of storage keys to query
19
+ * @param at - Optional block hash to query at (defaults to current/best block)
20
+ * @returns Promise resolving to a record mapping storage keys to their values
21
+ */
22
+ async query(keys, at) {
23
+ const client = this.client;
24
+ // Query storage at the specified block or current block
25
+ const changeSets = at
26
+ ? await client.rpc.state_queryStorageAt(keys, at)
27
+ : await client.rpc.state_queryStorageAt(keys);
28
+ // Create a map of key -> value for easy lookup
29
+ const results = {};
30
+ // Initialize all keys with undefined
31
+ keys.forEach((key) => (results[key] = undefined));
32
+ // Update with actual values from the response
33
+ if (changeSets && changeSets.length > 0) {
34
+ changeSets[0].changes.forEach(([key, value]) => {
35
+ results[key] = value ?? undefined;
36
+ });
37
+ }
38
+ return results;
39
+ }
40
+ /**
41
+ * Subscribe to multiple storage items using state_subscribeStorage
42
+ *
43
+ * @param keys - Array of storage keys to subscribe to
44
+ * @param callback - Function to call when storage values change
45
+ * @returns Promise resolving to an unsubscribe function
46
+ */
47
+ async subscribe(keys, callback) {
48
+ // Track the latest changes for each key
49
+ const lastChanges = {};
50
+ // Initialize all keys with undefined
51
+ keys.forEach((key) => (lastChanges[key] = undefined));
52
+ // Subscribe to storage changes
53
+ const client = this.client;
54
+ return client.rpc.state_subscribeStorage(keys, (changeSet) => {
55
+ // Update the latest changes
56
+ changeSet.changes.forEach(([key, value]) => {
57
+ if (lastChanges[key] !== value) {
58
+ lastChanges[key] = value ?? undefined;
59
+ }
60
+ });
61
+ // Call the callback with the updated map
62
+ callback({ ...lastChanges });
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,35 @@
1
+ import { BlockHash, StorageData, StorageKey } from '@dedot/codecs';
2
+ import type { Callback, RpcV2, Unsub, VersionedGenericSubstrateApi } from '@dedot/types';
3
+ import type { SubstrateApi } from '../chaintypes/index.js';
4
+ import { DedotClient } from '../client/DedotClient.js';
5
+ import { BaseStorageQuery } from './BaseStorageQuery.js';
6
+ /**
7
+ * @name NewStorageQuery
8
+ * @description
9
+ * Implementation of BaseStorageQuery for the new JSON-RPC API (v2).
10
+ * This service handles storage queries using the chainHead_storage RPC method
11
+ * and chainHead events for subscriptions.
12
+ *
13
+ * It provides:
14
+ * - One-time queries using chainHead_storage
15
+ * - Subscriptions using chainHead 'bestBlock' events
16
+ * - Efficient change detection for subscriptions
17
+ */
18
+ export declare class NewStorageQuery<ChainApi extends VersionedGenericSubstrateApi = SubstrateApi> extends BaseStorageQuery<RpcV2, ChainApi, DedotClient<ChainApi>> {
19
+ /**
20
+ * Query multiple storage items in a single call using chainHead_storage
21
+ *
22
+ * @param keys - Array of storage keys to query
23
+ * @param at - Optional block hash to query at (defaults to best block)
24
+ * @returns Promise resolving to a record mapping storage keys to their values
25
+ */
26
+ query(keys: StorageKey[], at?: BlockHash): Promise<Record<StorageKey, StorageData | undefined>>;
27
+ /**
28
+ * Subscribe to multiple storage items using chainHead 'bestBlock' events
29
+ *
30
+ * @param keys - Array of storage keys to subscribe to
31
+ * @param callback - Function to call when storage values change
32
+ * @returns Promise resolving to an unsubscribe function
33
+ */
34
+ subscribe(keys: StorageKey[], callback: Callback<Record<StorageKey, StorageData | undefined>>): Promise<Unsub>;
35
+ }