@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.
- package/chaintypes/substrate/consts.d.ts +2 -2
- package/chaintypes/substrate/consts.js +1 -0
- package/chaintypes/substrate/errors.d.ts +909 -909
- package/chaintypes/substrate/errors.js +1 -0
- package/chaintypes/substrate/events.d.ts +664 -664
- package/chaintypes/substrate/events.js +1 -0
- package/chaintypes/substrate/index.d.ts +13 -16
- package/chaintypes/substrate/index.js +1 -0
- package/chaintypes/substrate/json-rpc.d.ts +2 -2
- package/chaintypes/substrate/json-rpc.js +1 -0
- package/chaintypes/substrate/query.d.ts +509 -527
- package/chaintypes/substrate/query.js +1 -0
- package/chaintypes/substrate/runtime.d.ts +123 -123
- package/chaintypes/substrate/runtime.js +1 -0
- package/chaintypes/substrate/tx.d.ts +1311 -1311
- package/chaintypes/substrate/tx.js +1 -0
- package/chaintypes/substrate/types.js +1 -0
- package/chaintypes/substrate/view-functions.d.ts +2 -2
- package/chaintypes/substrate/view-functions.js +1 -0
- package/cjs/chaintypes/substrate/consts.js +1 -0
- package/cjs/chaintypes/substrate/errors.js +1 -0
- package/cjs/chaintypes/substrate/events.js +1 -0
- package/cjs/chaintypes/substrate/index.js +1 -0
- package/cjs/chaintypes/substrate/json-rpc.js +1 -0
- package/cjs/chaintypes/substrate/query.js +1 -0
- package/cjs/chaintypes/substrate/runtime.js +1 -0
- package/cjs/chaintypes/substrate/tx.js +1 -0
- package/cjs/chaintypes/substrate/types.js +1 -0
- package/cjs/chaintypes/substrate/view-functions.js +1 -0
- package/cjs/client/BaseSubstrateClient.js +45 -2
- package/cjs/client/DedotClient.js +356 -224
- package/cjs/client/LegacyClient.js +99 -23
- package/cjs/client/V2Client.js +316 -0
- package/cjs/client/explorer/LegacyBlockExplorer.js +263 -0
- package/cjs/client/explorer/V2BlockExplorer.js +74 -0
- package/cjs/client/explorer/index.js +18 -0
- package/cjs/client/utils.js +57 -0
- package/cjs/executor/EventExecutor.js +1 -0
- package/cjs/executor/RuntimeApiExecutor.js +49 -2
- package/cjs/executor/StorageQueryExecutor.js +114 -2
- package/cjs/executor/ViewFunctionExecutor.js +21 -2
- package/cjs/executor/v2/StorageQueryExecutorV2.js +7 -1
- package/cjs/executor/validation-helpers.js +181 -0
- package/cjs/extrinsic/submittable/BaseSubmittableExtrinsic.js +86 -2
- package/cjs/extrinsic/submittable/SubmittableExtrinsic.js +38 -16
- package/cjs/extrinsic/submittable/SubmittableExtrinsicV2.js +12 -2
- package/cjs/json-rpc/group/ChainHead/ChainHead.js +226 -21
- package/cjs/storage/QueryableStorage.js +1 -0
- package/client/BaseSubstrateClient.d.ts +34 -18
- package/client/BaseSubstrateClient.js +47 -4
- package/client/DedotClient.d.ts +346 -49
- package/client/DedotClient.js +356 -224
- package/client/LegacyClient.d.ts +22 -17
- package/client/LegacyClient.js +101 -25
- package/client/V2Client.d.ts +75 -0
- package/client/V2Client.js +312 -0
- package/client/explorer/LegacyBlockExplorer.d.ts +68 -0
- package/client/explorer/LegacyBlockExplorer.js +259 -0
- package/client/explorer/V2BlockExplorer.d.ts +40 -0
- package/client/explorer/V2BlockExplorer.js +70 -0
- package/client/explorer/index.d.ts +2 -0
- package/client/explorer/index.js +2 -0
- package/client/utils.d.ts +18 -0
- package/client/utils.js +52 -0
- package/executor/ConstantExecutor.d.ts +1 -2
- package/executor/ErrorExecutor.d.ts +2 -2
- package/executor/EventExecutor.d.ts +2 -2
- package/executor/EventExecutor.js +1 -0
- package/executor/Executor.d.ts +4 -5
- package/executor/RuntimeApiExecutor.d.ts +2 -2
- package/executor/RuntimeApiExecutor.js +27 -3
- package/executor/StorageQueryExecutor.d.ts +3 -2
- package/executor/StorageQueryExecutor.js +92 -3
- package/executor/TxExecutor.d.ts +2 -2
- package/executor/ViewFunctionExecutor.d.ts +2 -2
- package/executor/ViewFunctionExecutor.js +22 -3
- package/executor/v2/RuntimeApiExecutorV2.d.ts +2 -3
- package/executor/v2/StorageQueryExecutorV2.d.ts +3 -3
- package/executor/v2/StorageQueryExecutorV2.js +8 -2
- package/executor/v2/TxExecutorV2.d.ts +5 -5
- package/executor/v2/ViewFunctionExecutorV2.d.ts +2 -3
- package/executor/validation-helpers.d.ts +36 -0
- package/executor/validation-helpers.js +174 -0
- package/extrinsic/extensions/SignedExtension.d.ts +1 -1
- package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +7 -3
- package/extrinsic/submittable/BaseSubmittableExtrinsic.js +64 -3
- package/extrinsic/submittable/SubmittableExtrinsic.d.ts +4 -1
- package/extrinsic/submittable/SubmittableExtrinsic.js +39 -17
- package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +6 -3
- package/extrinsic/submittable/SubmittableExtrinsicV2.js +13 -3
- package/json-rpc/JsonRpcClient.d.ts +6 -6
- package/json-rpc/group/Archive.d.ts +1 -1
- package/json-rpc/group/ChainHead/ChainHead.d.ts +18 -2
- package/json-rpc/group/ChainHead/ChainHead.js +226 -21
- package/json-rpc/group/ChainSpec.d.ts +3 -3
- package/json-rpc/group/Transaction.d.ts +1 -1
- package/json-rpc/group/TransactionWatch.d.ts +1 -1
- package/package.json +9 -9
- package/proxychain.d.ts +3 -4
- package/storage/NewStorageQuery.d.ts +3 -3
- package/storage/QueryableStorage.js +1 -0
- 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,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;
|
package/client/utils.js
ADDED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
7
|
+
export declare class EventExecutor extends Executor {
|
|
8
8
|
#private;
|
|
9
9
|
doExecute(pallet: string, eventName: string): GenericPalletEvent;
|
|
10
10
|
}
|
package/executor/Executor.d.ts
CHANGED
|
@@ -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
|
|
13
|
+
export declare abstract class Executor {
|
|
15
14
|
#private;
|
|
16
|
-
readonly client: ISubstrateClientAt<
|
|
17
|
-
constructor(client: ISubstrateClientAt<
|
|
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<
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
23
|
-
const
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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);
|
package/executor/TxExecutor.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
18
|
-
const
|
|
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
|
|
10
|
+
export declare class RuntimeApiExecutorV2 extends RuntimeApiExecutor {
|
|
12
11
|
chainHead: ChainHead;
|
|
13
|
-
constructor(client: ISubstrateClientAt<
|
|
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
|
|
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
|
|
10
|
+
export declare class StorageQueryExecutorV2 extends StorageQueryExecutor {
|
|
11
11
|
chainHead: ChainHead;
|
|
12
|
-
constructor(client: ISubstrateClientAt<
|
|
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
|
-
|
|
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
|
|
2
|
-
import {
|
|
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
|
|
9
|
-
readonly client:
|
|
10
|
-
constructor(client:
|
|
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
|
|
9
|
+
export declare class ViewFunctionExecutorV2 extends ViewFunctionExecutor {
|
|
11
10
|
chainHead: ChainHead;
|
|
12
|
-
constructor(client: ISubstrateClientAt<
|
|
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;
|