@dedot/api 0.18.8 → 1.0.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 (102) hide show
  1. package/chaintypes/substrate/consts.d.ts +2 -2
  2. package/chaintypes/substrate/consts.js +1 -0
  3. package/chaintypes/substrate/errors.d.ts +909 -909
  4. package/chaintypes/substrate/errors.js +1 -0
  5. package/chaintypes/substrate/events.d.ts +664 -664
  6. package/chaintypes/substrate/events.js +1 -0
  7. package/chaintypes/substrate/index.d.ts +13 -16
  8. package/chaintypes/substrate/index.js +1 -0
  9. package/chaintypes/substrate/json-rpc.d.ts +2 -2
  10. package/chaintypes/substrate/json-rpc.js +1 -0
  11. package/chaintypes/substrate/query.d.ts +509 -527
  12. package/chaintypes/substrate/query.js +1 -0
  13. package/chaintypes/substrate/runtime.d.ts +123 -123
  14. package/chaintypes/substrate/runtime.js +1 -0
  15. package/chaintypes/substrate/tx.d.ts +1311 -1311
  16. package/chaintypes/substrate/tx.js +1 -0
  17. package/chaintypes/substrate/types.js +1 -0
  18. package/chaintypes/substrate/view-functions.d.ts +2 -2
  19. package/chaintypes/substrate/view-functions.js +1 -0
  20. package/cjs/chaintypes/substrate/consts.js +1 -0
  21. package/cjs/chaintypes/substrate/errors.js +1 -0
  22. package/cjs/chaintypes/substrate/events.js +1 -0
  23. package/cjs/chaintypes/substrate/index.js +1 -0
  24. package/cjs/chaintypes/substrate/json-rpc.js +1 -0
  25. package/cjs/chaintypes/substrate/query.js +1 -0
  26. package/cjs/chaintypes/substrate/runtime.js +1 -0
  27. package/cjs/chaintypes/substrate/tx.js +1 -0
  28. package/cjs/chaintypes/substrate/types.js +1 -0
  29. package/cjs/chaintypes/substrate/view-functions.js +1 -0
  30. package/cjs/client/BaseSubstrateClient.js +45 -2
  31. package/cjs/client/DedotClient.js +356 -224
  32. package/cjs/client/LegacyClient.js +99 -23
  33. package/cjs/client/V2Client.js +316 -0
  34. package/cjs/client/explorer/LegacyBlockExplorer.js +263 -0
  35. package/cjs/client/explorer/V2BlockExplorer.js +74 -0
  36. package/cjs/client/explorer/index.js +18 -0
  37. package/cjs/client/utils.js +57 -0
  38. package/cjs/executor/EventExecutor.js +1 -0
  39. package/cjs/executor/RuntimeApiExecutor.js +49 -2
  40. package/cjs/executor/StorageQueryExecutor.js +114 -2
  41. package/cjs/executor/ViewFunctionExecutor.js +21 -2
  42. package/cjs/executor/v2/StorageQueryExecutorV2.js +7 -1
  43. package/cjs/executor/validation-helpers.js +181 -0
  44. package/cjs/extrinsic/submittable/BaseSubmittableExtrinsic.js +86 -2
  45. package/cjs/extrinsic/submittable/SubmittableExtrinsic.js +38 -16
  46. package/cjs/extrinsic/submittable/SubmittableExtrinsicV2.js +12 -2
  47. package/cjs/json-rpc/group/ChainHead/ChainHead.js +226 -21
  48. package/cjs/storage/QueryableStorage.js +1 -0
  49. package/client/BaseSubstrateClient.d.ts +34 -18
  50. package/client/BaseSubstrateClient.js +47 -4
  51. package/client/DedotClient.d.ts +346 -49
  52. package/client/DedotClient.js +356 -224
  53. package/client/LegacyClient.d.ts +22 -17
  54. package/client/LegacyClient.js +101 -25
  55. package/client/V2Client.d.ts +75 -0
  56. package/client/V2Client.js +312 -0
  57. package/client/explorer/LegacyBlockExplorer.d.ts +68 -0
  58. package/client/explorer/LegacyBlockExplorer.js +259 -0
  59. package/client/explorer/V2BlockExplorer.d.ts +40 -0
  60. package/client/explorer/V2BlockExplorer.js +70 -0
  61. package/client/explorer/index.d.ts +2 -0
  62. package/client/explorer/index.js +2 -0
  63. package/client/utils.d.ts +18 -0
  64. package/client/utils.js +52 -0
  65. package/executor/ConstantExecutor.d.ts +1 -2
  66. package/executor/ErrorExecutor.d.ts +2 -2
  67. package/executor/EventExecutor.d.ts +2 -2
  68. package/executor/EventExecutor.js +1 -0
  69. package/executor/Executor.d.ts +4 -5
  70. package/executor/RuntimeApiExecutor.d.ts +2 -2
  71. package/executor/RuntimeApiExecutor.js +27 -3
  72. package/executor/StorageQueryExecutor.d.ts +3 -2
  73. package/executor/StorageQueryExecutor.js +92 -3
  74. package/executor/TxExecutor.d.ts +2 -2
  75. package/executor/ViewFunctionExecutor.d.ts +2 -2
  76. package/executor/ViewFunctionExecutor.js +22 -3
  77. package/executor/v2/RuntimeApiExecutorV2.d.ts +2 -3
  78. package/executor/v2/StorageQueryExecutorV2.d.ts +3 -3
  79. package/executor/v2/StorageQueryExecutorV2.js +8 -2
  80. package/executor/v2/TxExecutorV2.d.ts +5 -5
  81. package/executor/v2/ViewFunctionExecutorV2.d.ts +2 -3
  82. package/executor/validation-helpers.d.ts +36 -0
  83. package/executor/validation-helpers.js +174 -0
  84. package/extrinsic/extensions/SignedExtension.d.ts +1 -1
  85. package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +7 -3
  86. package/extrinsic/submittable/BaseSubmittableExtrinsic.js +64 -3
  87. package/extrinsic/submittable/SubmittableExtrinsic.d.ts +4 -1
  88. package/extrinsic/submittable/SubmittableExtrinsic.js +39 -17
  89. package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +6 -3
  90. package/extrinsic/submittable/SubmittableExtrinsicV2.js +13 -3
  91. package/json-rpc/JsonRpcClient.d.ts +6 -6
  92. package/json-rpc/group/Archive.d.ts +1 -1
  93. package/json-rpc/group/ChainHead/ChainHead.d.ts +18 -2
  94. package/json-rpc/group/ChainHead/ChainHead.js +226 -21
  95. package/json-rpc/group/ChainSpec.d.ts +3 -3
  96. package/json-rpc/group/Transaction.d.ts +1 -1
  97. package/json-rpc/group/TransactionWatch.d.ts +1 -1
  98. package/package.json +9 -9
  99. package/proxychain.d.ts +3 -4
  100. package/storage/NewStorageQuery.d.ts +3 -3
  101. package/storage/QueryableStorage.js +1 -0
  102. package/types.d.ts +94 -6
@@ -0,0 +1,70 @@
1
+ import { $Header } from '@dedot/codecs';
2
+ import { assert } from '@dedot/utils';
3
+ /**
4
+ * @name V2BlockExplorer
5
+ * @description Block explorer implementation for V2Client using JSON-RPC v2 (ChainHead API)
6
+ */
7
+ export class V2BlockExplorer {
8
+ #chainHead;
9
+ #archive;
10
+ constructor(client) {
11
+ this.#chainHead = client.chainHead;
12
+ this.#archive = client.archive;
13
+ }
14
+ /**
15
+ * Convert PinnedBlock to BlockInfo format
16
+ */
17
+ toBlockInfo(block) {
18
+ return {
19
+ hash: block.hash,
20
+ number: block.number,
21
+ parent: block.parent,
22
+ runtimeUpgraded: block.runtimeUpgraded === true,
23
+ };
24
+ }
25
+ best(callback) {
26
+ if (callback) {
27
+ // Subscribe mode
28
+ const handler = (block) => {
29
+ callback(this.toBlockInfo(block));
30
+ };
31
+ this.#chainHead.bestBlock().then((block) => {
32
+ handler(block);
33
+ });
34
+ return this.#chainHead.on('bestBlock', handler);
35
+ }
36
+ else {
37
+ // One-time query
38
+ return this.#chainHead.bestBlock().then((block) => this.toBlockInfo(block));
39
+ }
40
+ }
41
+ finalized(callback) {
42
+ if (callback) {
43
+ // Subscribe mode
44
+ const handler = (block) => {
45
+ callback(this.toBlockInfo(block));
46
+ };
47
+ this.#chainHead.finalizedBlock().then((block) => {
48
+ handler(block);
49
+ });
50
+ return this.#chainHead.on('finalizedBlock', handler);
51
+ }
52
+ else {
53
+ return this.#chainHead.finalizedBlock().then((block) => this.toBlockInfo(block));
54
+ }
55
+ }
56
+ /**
57
+ * Get the header of a block by number or hash
58
+ */
59
+ async header(hash) {
60
+ const rawHeader = await this.#chainHead.header(hash);
61
+ assert(rawHeader, `Header not found for block ${hash}`);
62
+ return $Header.tryDecode(rawHeader);
63
+ }
64
+ /**
65
+ * Get the body (transactions) of a block by number or hash
66
+ */
67
+ async body(hash) {
68
+ return await this.#chainHead.body(hash);
69
+ }
70
+ }
@@ -0,0 +1,2 @@
1
+ export * from './V2BlockExplorer.js';
2
+ export * from './LegacyBlockExplorer.js';
@@ -0,0 +1,2 @@
1
+ export * from './V2BlockExplorer.js';
2
+ export * from './LegacyBlockExplorer.js';
@@ -0,0 +1,18 @@
1
+ import { BlockHash, Header, Metadata } from '@dedot/codecs';
2
+ import { HashFn } from '@dedot/utils';
3
+ /**
4
+ * Detects the hasher from V16 metadata by looking at the System pallet's Hashing associated type
5
+ *
6
+ * @param metadata - The metadata to inspect
7
+ * @returns The detected HashFn or undefined if detection fails
8
+ */
9
+ export declare function detectHasherFromMetadata(metadata: Metadata): HashFn | undefined;
10
+ /**
11
+ * Detects the hasher by trying different hash functions on a block header
12
+ * and comparing the result with the expected block hash
13
+ *
14
+ * @param header - The block header to hash
15
+ * @param expectedHash - The expected block hash
16
+ * @returns The matching HashFn or undefined if no match is found
17
+ */
18
+ export declare function detectHasherFromHeader(header: Header, expectedHash: BlockHash): HashFn | undefined;
@@ -0,0 +1,52 @@
1
+ import { $Header } from '@dedot/codecs';
2
+ import { blake2_256, keccak_256, u8aToHex } from '@dedot/utils';
3
+ const HASHERS = {
4
+ BlakeTwo256: blake2_256, // commonly used in polkadot-sdk based-chains
5
+ Keccak256: keccak_256, // widely used in EVM-compatible chains, e.g: Hyperbridge
6
+ };
7
+ /**
8
+ * Detects the hasher from V16 metadata by looking at the System pallet's Hashing associated type
9
+ *
10
+ * @param metadata - The metadata to inspect
11
+ * @returns The detected HashFn or undefined if detection fails
12
+ */
13
+ export function detectHasherFromMetadata(metadata) {
14
+ try {
15
+ const latest = metadata.latest;
16
+ // Find the System pallet
17
+ const systemPallet = latest.pallets.find((p) => p.name === 'System');
18
+ if (!systemPallet)
19
+ return undefined;
20
+ // Find the Hashing associated type
21
+ const hashingType = systemPallet.associatedTypes.find((t) => t.name === 'Hashing');
22
+ if (!hashingType)
23
+ return undefined;
24
+ // Get the type definition
25
+ const typeDef = latest.types[hashingType.typeId];
26
+ if (!typeDef)
27
+ return undefined;
28
+ // Get the hasher name from the type path (last element)
29
+ const hasherName = typeDef.path.at(-1);
30
+ if (!hasherName)
31
+ return undefined;
32
+ return HASHERS[hasherName];
33
+ }
34
+ catch { }
35
+ return undefined;
36
+ }
37
+ /**
38
+ * Detects the hasher by trying different hash functions on a block header
39
+ * and comparing the result with the expected block hash
40
+ *
41
+ * @param header - The block header to hash
42
+ * @param expectedHash - The expected block hash
43
+ * @returns The matching HashFn or undefined if no match is found
44
+ */
45
+ export function detectHasherFromHeader(header, expectedHash) {
46
+ try {
47
+ const encodedHeader = $Header.tryEncode(header);
48
+ return Object.values(HASHERS).find((h) => u8aToHex(h(encodedHeader)) === expectedHash);
49
+ }
50
+ catch { }
51
+ return undefined;
52
+ }
@@ -1,10 +1,9 @@
1
- import { GenericSubstrateApi } from '@dedot/types';
2
1
  import { Executor } from './Executor.js';
3
2
  /**
4
3
  * @name ConstantExecutor
5
4
  *
6
5
  * Find & decode the constant value from metadata
7
6
  */
8
- export declare class ConstantExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
7
+ export declare class ConstantExecutor extends Executor {
9
8
  doExecute(pallet: string, constantName: string): unknown;
10
9
  }
@@ -1,10 +1,10 @@
1
- import type { GenericPalletError, GenericSubstrateApi } from '@dedot/types';
1
+ import type { GenericPalletError } from '@dedot/types';
2
2
  import { Executor } from './Executor.js';
3
3
  /**
4
4
  * @name ErrorExecutor
5
5
  * @description Find pallet error information from metadata
6
6
  */
7
- export declare class ErrorExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
7
+ export declare class ErrorExecutor extends Executor {
8
8
  #private;
9
9
  doExecute(pallet: string, errorName: string): GenericPalletError;
10
10
  }
@@ -1,10 +1,10 @@
1
- import type { GenericPalletEvent, GenericSubstrateApi } from '@dedot/types';
1
+ import type { GenericPalletEvent } from '@dedot/types';
2
2
  import { Executor } from './Executor.js';
3
3
  /**
4
4
  * @name EventExecutor
5
5
  * @description Find pallet event information from metadata
6
6
  */
7
- export declare class EventExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
7
+ export declare class EventExecutor extends Executor {
8
8
  #private;
9
9
  doExecute(pallet: string, eventName: string): GenericPalletEvent;
10
10
  }
@@ -56,6 +56,7 @@ export class EventExecutor extends Executor {
56
56
  palletIndex: targetPallet.index,
57
57
  };
58
58
  return {
59
+ // @ts-ignore
59
60
  is,
60
61
  find,
61
62
  filter,
@@ -1,5 +1,4 @@
1
1
  import type { BlockHash, PalletDefLatest } from '@dedot/codecs';
2
- import type { GenericSubstrateApi } from '@dedot/types';
3
2
  import { HexString } from '@dedot/utils';
4
3
  import { ISubstrateClient, ISubstrateClientAt } from '../types.js';
5
4
  export interface StateCallParams {
@@ -11,12 +10,12 @@ export interface StateCallParams {
11
10
  * @name Executor
12
11
  * @description Execution abstraction for a specific action
13
12
  */
14
- export declare abstract class Executor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> {
13
+ export declare abstract class Executor {
15
14
  #private;
16
- readonly client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>;
17
- constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, atBlockHash?: BlockHash);
15
+ readonly client: ISubstrateClientAt<any> | ISubstrateClient<any, any>;
16
+ constructor(client: ISubstrateClientAt<any> | ISubstrateClient<any, any>, atBlockHash?: BlockHash);
18
17
  get atBlockHash(): any;
19
- get registry(): import("@dedot/codecs").PortableRegistry<ChainApi["types"]>;
18
+ get registry(): import("@dedot/codecs").PortableRegistry<any>;
20
19
  get metadata(): {
21
20
  types: {
22
21
  id: number;
@@ -1,4 +1,4 @@
1
- import type { GenericRuntimeApiMethod, GenericSubstrateApi, RuntimeApiMethodParamSpec, RuntimeApiMethodSpec, RuntimeApiSpec } from '@dedot/types';
1
+ import type { GenericRuntimeApiMethod, RuntimeApiMethodParamSpec, RuntimeApiMethodSpec, RuntimeApiSpec } from '@dedot/types';
2
2
  import { Executor } from './Executor.js';
3
3
  export declare const FallbackRuntimeApis: Record<string, number>;
4
4
  export declare const FallbackRuntimeApiSpecs: {
@@ -11,7 +11,7 @@ export declare const FallbackRuntimeApiSpecs: {
11
11
  * or defined externally when initializing `Dedot` instance
12
12
  * via `ApiOptions.runtimeApis` option.
13
13
  */
14
- export declare class RuntimeApiExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
14
+ export declare class RuntimeApiExecutor extends Executor {
15
15
  #private;
16
16
  doExecute(runtimeApi: string, method: string): GenericRuntimeApiMethod;
17
17
  tryDecode(callSpec: RuntimeApiMethodSpec, raw: any): unknown;
@@ -1,6 +1,8 @@
1
1
  import { Metadata, toRuntimeApiMethods, toRuntimeApiSpecs } from '@dedot/runtime-specs';
2
- import { assert, calcRuntimeApiHash, concatU8a, isNumber, stringPascalCase, stringSnakeCase, u8aToHex, UnknownApiError, } from '@dedot/utils';
2
+ import * as $ from '@dedot/shape';
3
+ import { assert, calcRuntimeApiHash, isNumber, stringPascalCase, stringSnakeCase, u8aToHex, UnknownApiError, } from '@dedot/utils';
3
4
  import { Executor } from './Executor.js';
5
+ import { buildCompatibilityError, padArgsForOptionalParams } from './validation-helpers.js';
4
6
  export const FallbackRuntimeApis = { '0x37e397fc7c91f5e4': 2 };
5
7
  export const FallbackRuntimeApiSpecs = { Metadata };
6
8
  /**
@@ -19,8 +21,30 @@ export class RuntimeApiExecutor extends Executor {
19
21
  assert(callSpec, new UnknownApiError(`Runtime api spec not found for ${callName}`));
20
22
  const callFn = async (...args) => {
21
23
  const { params } = callSpec;
22
- const formattedInputs = params.map((param, index) => this.tryEncode(param, args[index]));
23
- const bytes = u8aToHex(concatU8a(...formattedInputs));
24
+ // Pad args with undefined for missing trailing optional parameters
25
+ const withCodecs = params.every((p) => !!p.codec);
26
+ const paddedArgs = padArgsForOptionalParams(args, params, withCodecs
27
+ ? {} // intentional trick to pass-by client initialization
28
+ : this.registry);
29
+ const $ParamsTuple = $.Tuple(...params.map((param) => this.#findCodec(param, // --
30
+ `Codec not found for param ${param.name}`)));
31
+ // Enhanced validation with detailed error messages
32
+ try {
33
+ $ParamsTuple.assert?.(paddedArgs);
34
+ }
35
+ catch (error) {
36
+ // Enhance Shape assertion errors with detailed compatibility information
37
+ if (error instanceof $.ShapeAssertError) {
38
+ throw buildCompatibilityError(error, // --
39
+ params, args, {
40
+ apiName: `${runtimeApi}.${method}`,
41
+ registry: this.registry,
42
+ });
43
+ }
44
+ throw error;
45
+ }
46
+ const formattedInputs = $ParamsTuple.tryEncode(paddedArgs);
47
+ const bytes = u8aToHex(formattedInputs);
24
48
  const callParams = {
25
49
  func: callName,
26
50
  params: bytes,
@@ -1,12 +1,13 @@
1
1
  import { BlockHash, Option, StorageData, StorageKey } from '@dedot/codecs';
2
- import type { AsyncMethod, Callback, GenericStorageQuery, GenericSubstrateApi, Unsub } from '@dedot/types';
2
+ import type { AsyncMethod, Callback, GenericStorageQuery, Unsub } from '@dedot/types';
3
3
  import { type BaseStorageQuery, QueryableStorage } from '../storage/index.js';
4
4
  import { Executor } from './Executor.js';
5
5
  /**
6
6
  * @name StorageQueryExecutor
7
7
  * @description Execute a query to on-chain storage
8
8
  */
9
- export declare class StorageQueryExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
9
+ export declare class StorageQueryExecutor extends Executor {
10
+ #private;
10
11
  doExecute(pallet: string, storage: string): GenericStorageQuery;
11
12
  protected exposeStorageMapMethods(entry: QueryableStorage): Record<string, AsyncMethod>;
12
13
  protected getStorageQuery(): BaseStorageQuery;
@@ -1,6 +1,8 @@
1
- import { assert, isFunction, isObject } from '@dedot/utils';
1
+ import * as $ from '@dedot/shape';
2
+ import { assert, isFunction, isObject, stringCamelCase } from '@dedot/utils';
2
3
  import { LegacyStorageQuery, QueryableStorage } from '../storage/index.js';
3
4
  import { Executor } from './Executor.js';
5
+ import { buildCompatibilityError } from './validation-helpers.js';
4
6
  const DEFAULT_KEYS_PAGE_SIZE = 1000;
5
7
  const DEFAULT_ENTRIES_PAGE_SIZE = 250;
6
8
  /**
@@ -29,6 +31,8 @@ export class StorageQueryExecutor extends Executor {
29
31
  };
30
32
  const queryFn = async (...args) => {
31
33
  const [inArgs, callback] = extractArgs(args);
34
+ // Validate storage inputs before encoding
35
+ this.#validateStorageInputs(entry, inArgs.at(0));
32
36
  const encodedKey = entry.encodeKey(inArgs.at(0));
33
37
  // if a callback is passed, make a storage subscription and return an unsub function
34
38
  if (callback) {
@@ -55,6 +59,10 @@ export class StorageQueryExecutor extends Executor {
55
59
  const [inArgs, callback] = extractArgs(args);
56
60
  const multiArgs = inArgs.at(0);
57
61
  assert(Array.isArray(multiArgs), 'First param for multi query should be an array');
62
+ // Validate each argument before encoding
63
+ multiArgs.forEach((arg, index) => {
64
+ this.#validateStorageInputs(entry, arg, index);
65
+ });
58
66
  const encodedKeys = multiArgs.map((arg) => entry.encodeKey(arg));
59
67
  // if a callback is passed, make a storage subscription and return an unsub function
60
68
  if (callback) {
@@ -76,7 +84,7 @@ export class StorageQueryExecutor extends Executor {
76
84
  exposeStorageMapMethods(entry) {
77
85
  const rawKeys = async (partialInput, pagination) => {
78
86
  const pageSize = pagination?.pageSize || DEFAULT_KEYS_PAGE_SIZE;
79
- const startKey = pagination?.startKey || entry.prefixKey;
87
+ const startKey = pagination?.startKey;
80
88
  return await this.client.rpc.state_getKeysPaged(entry.encodeKey(partialInput, true), pageSize, startKey, this.atBlockHash);
81
89
  };
82
90
  const extractArgs = (args) => {
@@ -96,7 +104,88 @@ export class StorageQueryExecutor extends Executor {
96
104
  const storageMap = await this.queryStorage(storageKeys, this.atBlockHash);
97
105
  return storageKeys.map((key) => [entry.decodeKey(key), entry.decodeValue(storageMap[key])]);
98
106
  };
99
- return { pagedKeys, pagedEntries };
107
+ const entries = async (...args) => {
108
+ const [inArgs] = extractArgs(args);
109
+ const allEntries = [];
110
+ let startKey;
111
+ while (true) {
112
+ const pagination = {
113
+ pageSize: DEFAULT_ENTRIES_PAGE_SIZE,
114
+ ...(startKey ? { startKey } : {}),
115
+ };
116
+ const storageKeys = await rawKeys(inArgs, pagination);
117
+ if (storageKeys.length === 0)
118
+ break;
119
+ const storageMap = await this.queryStorage(storageKeys, this.atBlockHash);
120
+ const pageEntries = storageKeys.map((key) => [
121
+ entry.decodeKey(key),
122
+ entry.decodeValue(storageMap[key]),
123
+ ]);
124
+ allEntries.push(...pageEntries);
125
+ if (storageKeys.length < DEFAULT_ENTRIES_PAGE_SIZE)
126
+ break;
127
+ startKey = storageKeys[storageKeys.length - 1];
128
+ }
129
+ return allEntries;
130
+ };
131
+ return { entries, pagedKeys, pagedEntries };
132
+ }
133
+ /**
134
+ * Validate storage query inputs before encoding
135
+ * @param entry - QueryableStorage instance
136
+ * @param keyInput - Input keys to validate
137
+ * @param itemIndex - Optional index for multi query error messages
138
+ */
139
+ #validateStorageInputs(entry, keyInput, itemIndex) {
140
+ const { storageType } = entry.storageEntry;
141
+ // Plain storage doesn't require validation
142
+ if (storageType.type === 'Plain') {
143
+ return;
144
+ }
145
+ if (storageType.type === 'Map') {
146
+ const { hashers, keyTypeId } = storageType.value;
147
+ const requiredKeys = hashers.length;
148
+ // Extract keyTypeIds for multi-key storage
149
+ let keyTypeIds = [keyTypeId];
150
+ if (hashers.length > 1) {
151
+ const { typeDef } = this.registry.findType(keyTypeId);
152
+ assert(typeDef.type === 'Tuple', 'Key type should be a tuple!');
153
+ keyTypeIds = typeDef.value.fields;
154
+ }
155
+ // Normalize input to array format
156
+ let keys;
157
+ if (requiredKeys === 1) {
158
+ // Single key: accept both single value or array
159
+ keys = Array.isArray(keyInput) ? keyInput : [keyInput];
160
+ }
161
+ else {
162
+ // Multiple keys: must be array
163
+ assert(Array.isArray(keyInput), 'Multi-key storage requires array input');
164
+ keys = keyInput;
165
+ }
166
+ // Build parameter specs for error messages
167
+ const paramSpecs = keyTypeIds.map((typeId, index) => ({
168
+ name: `key${index}`,
169
+ typeId,
170
+ }));
171
+ // Create tuple codec for validation
172
+ const $ParamsTuple = $.Tuple(...keyTypeIds.map((id) => this.registry.findCodec(id)));
173
+ // Validate inputs
174
+ try {
175
+ $ParamsTuple.assert?.(keys);
176
+ }
177
+ catch (error) {
178
+ if (error instanceof $.ShapeAssertError) {
179
+ throw buildCompatibilityError(error, // -
180
+ paramSpecs, keys, {
181
+ apiName: `${stringCamelCase(entry.pallet.name)}.${stringCamelCase(entry.storageEntry.name)}`,
182
+ registry: this.registry,
183
+ itemIndex,
184
+ });
185
+ }
186
+ throw error;
187
+ }
188
+ }
100
189
  }
101
190
  getStorageQuery() {
102
191
  return new LegacyStorageQuery(this.client);
@@ -1,10 +1,10 @@
1
- import type { GenericSubstrateApi, GenericTxCall, IRuntimeTxCall } from '@dedot/types';
1
+ import type { GenericTxCall, IRuntimeTxCall } from '@dedot/types';
2
2
  import { Executor } from './Executor.js';
3
3
  /**
4
4
  * @name TxExecutor
5
5
  * @description Execute a transaction instruction, returns a submittable extrinsic
6
6
  */
7
- export declare class TxExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
7
+ export declare class TxExecutor extends Executor {
8
8
  doExecute(pallet: string, functionName: string): GenericTxCall;
9
9
  protected createExtrinsic(call: IRuntimeTxCall): any;
10
10
  }
@@ -1,10 +1,10 @@
1
- import type { GenericSubstrateApi, GenericViewFunction } from '@dedot/types';
1
+ import type { GenericViewFunction } from '@dedot/types';
2
2
  import { Executor } from './Executor.js';
3
3
  /**
4
4
  * @name ViewFunctionExecutor
5
5
  * @description Execute view functions using the runtimeViewFunction API
6
6
  */
7
- export declare class ViewFunctionExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
7
+ export declare class ViewFunctionExecutor extends Executor {
8
8
  #private;
9
9
  doExecute(pallet: string, viewFunction: string): GenericViewFunction;
10
10
  }
@@ -1,6 +1,7 @@
1
1
  import * as $ from '@dedot/shape';
2
- import { assert, concatU8a, stringCamelCase, u8aToHex, UnknownApiError, DedotError } from '@dedot/utils';
2
+ import { assert, concatU8a, DedotError, stringCamelCase, u8aToHex, UnknownApiError } from '@dedot/utils';
3
3
  import { Executor } from './Executor.js';
4
+ import { buildCompatibilityError, padArgsForOptionalParams } from './validation-helpers.js';
4
5
  const RUNTIME_API_NAME = 'RuntimeViewFunction';
5
6
  const METHOD_NAME = 'execute_view_function';
6
7
  /**
@@ -14,8 +15,26 @@ export class ViewFunctionExecutor extends Executor {
14
15
  assert(viewFunctionDef, new UnknownApiError(`View function not found: ${pallet}.${viewFunction}`));
15
16
  const callFn = async (...args) => {
16
17
  const { inputs, id, output } = viewFunctionDef;
17
- const formattedInputs = inputs.map(({ typeId }, index) => this.registry.findCodec(typeId).tryEncode(args[index]));
18
- const bytes = u8aToHex(concatU8a(id, $.Vec($.u8).tryEncode(concatU8a(...formattedInputs))));
18
+ // Pad args with undefined for missing trailing optional parameters
19
+ const paddedArgs = padArgsForOptionalParams(args, inputs, this.registry);
20
+ const $ParamsTuple = $.Tuple(...inputs.map((param) => this.registry.findCodec(param.typeId)));
21
+ // Enhanced validation with detailed error messages
22
+ try {
23
+ $ParamsTuple.assert?.(paddedArgs);
24
+ }
25
+ catch (error) {
26
+ // Enhance Shape assertion errors with detailed compatibility information
27
+ if (error instanceof $.ShapeAssertError) {
28
+ throw buildCompatibilityError(error, // --
29
+ inputs, args, {
30
+ apiName: `${pallet}.${viewFunction}`,
31
+ registry: this.registry,
32
+ });
33
+ }
34
+ throw error;
35
+ }
36
+ const formattedInputs = $ParamsTuple.tryEncode(paddedArgs);
37
+ const bytes = u8aToHex(concatU8a(id, $.Vec($.u8).tryEncode(formattedInputs)));
19
38
  const func = `${RUNTIME_API_NAME}_${METHOD_NAME}`;
20
39
  const callParams = {
21
40
  func,
@@ -1,5 +1,4 @@
1
1
  import type { BlockHash } from '@dedot/codecs';
2
- import type { GenericSubstrateApi } from '@dedot/types';
3
2
  import { HexString } from '@dedot/utils';
4
3
  import { ChainHead } from '../../json-rpc/index.js';
5
4
  import { ISubstrateClient, ISubstrateClientAt } from '../../types.js';
@@ -8,8 +7,8 @@ import { RuntimeApiExecutor } from '../RuntimeApiExecutor.js';
8
7
  /**
9
8
  * @name RuntimeApiExecutorV2
10
9
  */
11
- export declare class RuntimeApiExecutorV2<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends RuntimeApiExecutor<ChainApi> {
10
+ export declare class RuntimeApiExecutorV2 extends RuntimeApiExecutor {
12
11
  chainHead: ChainHead;
13
- constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, chainHead: ChainHead, atBlockHash?: BlockHash);
12
+ constructor(client: ISubstrateClientAt<any> | ISubstrateClient<any, any>, chainHead: ChainHead, atBlockHash?: BlockHash);
14
13
  protected stateCall(callParams: StateCallParams): Promise<HexString>;
15
14
  }
@@ -1,5 +1,5 @@
1
1
  import { BlockHash } from '@dedot/codecs';
2
- import type { AsyncMethod, GenericSubstrateApi } from '@dedot/types';
2
+ import type { AsyncMethod } from '@dedot/types';
3
3
  import { ChainHead } from '../../json-rpc/index.js';
4
4
  import { type BaseStorageQuery, QueryableStorage } from '../../storage/index.js';
5
5
  import { ISubstrateClient, ISubstrateClientAt } from '../../types.js';
@@ -7,9 +7,9 @@ import { StorageQueryExecutor } from '../StorageQueryExecutor.js';
7
7
  /**
8
8
  * @name StorageQueryExecutorV2
9
9
  */
10
- export declare class StorageQueryExecutorV2<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends StorageQueryExecutor<ChainApi> {
10
+ export declare class StorageQueryExecutorV2 extends StorageQueryExecutor {
11
11
  chainHead: ChainHead;
12
- constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, chainHead: ChainHead, atBlockHash?: BlockHash);
12
+ constructor(client: ISubstrateClientAt<any> | ISubstrateClient<any, any>, chainHead: ChainHead, atBlockHash?: BlockHash);
13
13
  protected exposeStorageMapMethods(entry: QueryableStorage): Record<string, AsyncMethod>;
14
14
  protected getStorageQuery(): BaseStorageQuery;
15
15
  }
@@ -1,4 +1,4 @@
1
- import { assert } from '@dedot/utils';
1
+ import { assert, DedotError } from '@dedot/utils';
2
2
  import { NewStorageQuery } from '../../storage/index.js';
3
3
  import { StorageQueryExecutor } from '../StorageQueryExecutor.js';
4
4
  /**
@@ -25,7 +25,13 @@ export class StorageQueryExecutorV2 extends StorageQueryExecutor {
25
25
  entry.decodeValue(value),
26
26
  ]);
27
27
  };
28
- return { entries };
28
+ const pagedKeys = () => {
29
+ throw new DedotError('The pagedKeys method is not yet available via the new JSON-RPC (v2) specification. Please use the legacy client instead.');
30
+ };
31
+ const pagedEntries = () => {
32
+ throw new DedotError('The pagedEntries method is not yet available via the new JSON-RPC (v2) specification. Please use the legacy client instead.');
33
+ };
34
+ return { entries, pagedKeys, pagedEntries };
29
35
  }
30
36
  getStorageQuery() {
31
37
  // @ts-ignore little trick to make querying data client.at instance works here,
@@ -1,12 +1,12 @@
1
- import { IRuntimeTxCall, RpcVersion, VersionedGenericSubstrateApi } from '@dedot/types';
2
- import { DedotClient } from '../../client/index.js';
1
+ import { IRuntimeTxCall } from '@dedot/types';
2
+ import { V2Client } from '../../client/V2Client.js';
3
3
  import { TxExecutor } from '../TxExecutor.js';
4
4
  /**
5
5
  * @name TxExecutor
6
6
  * @description Execute a transaction instruction, returns a submittable extrinsic
7
7
  */
8
- export declare class TxExecutorV2<ChainApi extends VersionedGenericSubstrateApi = VersionedGenericSubstrateApi> extends TxExecutor<ChainApi[RpcVersion]> {
9
- readonly client: DedotClient<ChainApi>;
10
- constructor(client: DedotClient<ChainApi>);
8
+ export declare class TxExecutorV2 extends TxExecutor {
9
+ readonly client: V2Client<any>;
10
+ constructor(client: V2Client<any>);
11
11
  protected createExtrinsic(call: IRuntimeTxCall): any;
12
12
  }
@@ -1,4 +1,3 @@
1
- import type { GenericSubstrateApi } from '@dedot/types';
2
1
  import { HexString } from '@dedot/utils';
3
2
  import { ChainHead } from '../../json-rpc/index.js';
4
3
  import { ISubstrateClient, ISubstrateClientAt } from '../../types.js';
@@ -7,8 +6,8 @@ import { ViewFunctionExecutor } from '../ViewFunctionExecutor.js';
7
6
  /**
8
7
  * @name ViewFunctionExecutorV2
9
8
  */
10
- export declare class ViewFunctionExecutorV2<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends ViewFunctionExecutor<ChainApi> {
9
+ export declare class ViewFunctionExecutorV2 extends ViewFunctionExecutor {
11
10
  chainHead: ChainHead;
12
- constructor(client: ISubstrateClientAt<ChainApi> | ISubstrateClient<ChainApi, any>, chainHead: ChainHead);
11
+ constructor(client: ISubstrateClientAt<any> | ISubstrateClient<any, any>, chainHead: ChainHead);
13
12
  protected stateCall(callParams: StateCallParams): Promise<HexString>;
14
13
  }
@@ -0,0 +1,36 @@
1
+ import type { TypeRegistry } from '@dedot/codecs';
2
+ import type { AnyShape } from '@dedot/shape';
3
+ import { ApiCompatibilityError } from '@dedot/utils';
4
+ /**
5
+ * Parameter specification for validation
6
+ */
7
+ export interface ParamSpec {
8
+ name: string;
9
+ typeId?: number;
10
+ codec?: AnyShape;
11
+ }
12
+ /**
13
+ * Context for error messages
14
+ */
15
+ export interface ValidationContext {
16
+ apiName: string;
17
+ registry: TypeRegistry;
18
+ itemIndex?: number;
19
+ }
20
+ /**
21
+ * Check if a codec is an Option type
22
+ */
23
+ export declare function isOptionCodec(codec: AnyShape): boolean;
24
+ /**
25
+ * Check if a parameter is optional (wrapped in Option)
26
+ */
27
+ export declare function isOptionalParam(param: ParamSpec, registry: TypeRegistry): boolean;
28
+ /**
29
+ * Pad args array with undefined for missing trailing optional parameters.
30
+ * This allows users to omit optional parameters at the end of the parameter list.
31
+ */
32
+ export declare function padArgsForOptionalParams<T extends ParamSpec>(args: any[], params: T[], registry: TypeRegistry): any[];
33
+ /**
34
+ * Build a comprehensive API compatibility error with detailed information
35
+ */
36
+ export declare function buildCompatibilityError(originalError: Error, params: ParamSpec[], args: any[], context: ValidationContext): ApiCompatibilityError;