dedot 0.0.1-alpha.22
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/LICENSE +201 -0
- package/README.md +245 -0
- package/cjs/client/Dedot.js +444 -0
- package/cjs/client/index.js +17 -0
- package/cjs/executor/ConstantExecutor.js +22 -0
- package/cjs/executor/ErrorExecutor.js +47 -0
- package/cjs/executor/EventExecutor.js +53 -0
- package/cjs/executor/Executor.js +39 -0
- package/cjs/executor/RpcExecutor.js +109 -0
- package/cjs/executor/RuntimeApiExecutor.js +93 -0
- package/cjs/executor/StorageQueryExecutor.js +33 -0
- package/cjs/executor/TxExecutor.js +153 -0
- package/cjs/executor/index.js +24 -0
- package/cjs/extrinsic/SubmittableResult.js +33 -0
- package/cjs/extrinsic/extensions/ExtraSignedExtension.js +65 -0
- package/cjs/extrinsic/extensions/SignedExtension.js +41 -0
- package/cjs/extrinsic/extensions/index.js +19 -0
- package/cjs/extrinsic/extensions/known/ChargeAssetTxPayment.js +44 -0
- package/cjs/extrinsic/extensions/known/ChargeTransactionPayment.js +16 -0
- package/cjs/extrinsic/extensions/known/CheckGenesis.js +19 -0
- package/cjs/extrinsic/extensions/known/CheckMortality.js +71 -0
- package/cjs/extrinsic/extensions/known/CheckNonZeroSender.js +10 -0
- package/cjs/extrinsic/extensions/known/CheckNonce.js +34 -0
- package/cjs/extrinsic/extensions/known/CheckSpecVersion.js +19 -0
- package/cjs/extrinsic/extensions/known/CheckTxVersion.js +19 -0
- package/cjs/extrinsic/extensions/known/CheckWeight.js +10 -0
- package/cjs/extrinsic/extensions/known/PrevalidateAttests.js +11 -0
- package/cjs/extrinsic/extensions/known/index.js +25 -0
- package/cjs/extrinsic/index.js +18 -0
- package/cjs/index.js +25 -0
- package/cjs/package.json +1 -0
- package/cjs/packageInfo.js +5 -0
- package/cjs/proxychain.js +20 -0
- package/cjs/storage/QueryableStorage.js +99 -0
- package/cjs/types.js +2 -0
- package/client/Dedot.d.ts +181 -0
- package/client/Dedot.js +440 -0
- package/client/index.d.ts +1 -0
- package/client/index.js +1 -0
- package/executor/ConstantExecutor.d.ts +11 -0
- package/executor/ConstantExecutor.js +18 -0
- package/executor/ErrorExecutor.d.ts +11 -0
- package/executor/ErrorExecutor.js +43 -0
- package/executor/EventExecutor.d.ts +11 -0
- package/executor/EventExecutor.js +49 -0
- package/executor/Executor.d.ts +150 -0
- package/executor/Executor.js +35 -0
- package/executor/RpcExecutor.d.ts +14 -0
- package/executor/RpcExecutor.js +105 -0
- package/executor/RuntimeApiExecutor.d.ts +9 -0
- package/executor/RuntimeApiExecutor.js +89 -0
- package/executor/StorageQueryExecutor.d.ts +10 -0
- package/executor/StorageQueryExecutor.js +29 -0
- package/executor/TxExecutor.d.ts +47 -0
- package/executor/TxExecutor.js +147 -0
- package/executor/index.d.ts +8 -0
- package/executor/index.js +8 -0
- package/extrinsic/SubmittableResult.d.ts +19 -0
- package/extrinsic/SubmittableResult.js +29 -0
- package/extrinsic/extensions/ExtraSignedExtension.d.ts +14 -0
- package/extrinsic/extensions/ExtraSignedExtension.js +61 -0
- package/extrinsic/extensions/SignedExtension.d.ts +40 -0
- package/extrinsic/extensions/SignedExtension.js +37 -0
- package/extrinsic/extensions/index.d.ts +3 -0
- package/extrinsic/extensions/index.js +3 -0
- package/extrinsic/extensions/known/ChargeAssetTxPayment.d.ts +14 -0
- package/extrinsic/extensions/known/ChargeAssetTxPayment.js +40 -0
- package/extrinsic/extensions/known/ChargeTransactionPayment.d.ts +6 -0
- package/extrinsic/extensions/known/ChargeTransactionPayment.js +12 -0
- package/extrinsic/extensions/known/CheckGenesis.d.ts +10 -0
- package/extrinsic/extensions/known/CheckGenesis.js +15 -0
- package/extrinsic/extensions/known/CheckMortality.d.ts +15 -0
- package/extrinsic/extensions/known/CheckMortality.js +67 -0
- package/extrinsic/extensions/known/CheckNonZeroSender.d.ts +6 -0
- package/extrinsic/extensions/known/CheckNonZeroSender.js +6 -0
- package/extrinsic/extensions/known/CheckNonce.d.ts +10 -0
- package/extrinsic/extensions/known/CheckNonce.js +30 -0
- package/extrinsic/extensions/known/CheckSpecVersion.d.ts +9 -0
- package/extrinsic/extensions/known/CheckSpecVersion.js +15 -0
- package/extrinsic/extensions/known/CheckTxVersion.d.ts +9 -0
- package/extrinsic/extensions/known/CheckTxVersion.js +15 -0
- package/extrinsic/extensions/known/CheckWeight.d.ts +6 -0
- package/extrinsic/extensions/known/CheckWeight.js +6 -0
- package/extrinsic/extensions/known/PrevalidateAttests.d.ts +7 -0
- package/extrinsic/extensions/known/PrevalidateAttests.js +7 -0
- package/extrinsic/extensions/known/index.d.ts +3 -0
- package/extrinsic/extensions/known/index.js +22 -0
- package/extrinsic/index.d.ts +2 -0
- package/extrinsic/index.js +2 -0
- package/index.d.ts +6 -0
- package/index.js +5 -0
- package/package.json +54 -0
- package/packageInfo.d.ts +4 -0
- package/packageInfo.js +3 -0
- package/proxychain.d.ts +7 -0
- package/proxychain.js +16 -0
- package/storage/QueryableStorage.d.ts +16 -0
- package/storage/QueryableStorage.js +95 -0
- package/types.d.ts +44 -0
- package/types.js +1 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { SubstrateApi } from '@dedot/chaintypes';
|
|
2
|
+
import { BlockHash, PalletDefLatest } from '@dedot/codecs';
|
|
3
|
+
import { GenericSubstrateApi } from '@dedot/types';
|
|
4
|
+
import { Dedot } from '../client';
|
|
5
|
+
/**
|
|
6
|
+
* @name Executor
|
|
7
|
+
* @description Execution abstraction for a specific action
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class Executor<ChainApi extends GenericSubstrateApi = SubstrateApi> {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(api: Dedot<ChainApi>, atBlockHash?: BlockHash);
|
|
12
|
+
get api(): Dedot<ChainApi>;
|
|
13
|
+
get atBlockHash(): `0x${string}` | undefined;
|
|
14
|
+
get provider(): import("@polkadot/rpc-provider/types").ProviderInterface;
|
|
15
|
+
get registry(): import("@dedot/codecs").CodecRegistry;
|
|
16
|
+
get metadata(): {
|
|
17
|
+
types: {
|
|
18
|
+
id: number;
|
|
19
|
+
path: string[];
|
|
20
|
+
params: {
|
|
21
|
+
name: string;
|
|
22
|
+
typeId: number | undefined;
|
|
23
|
+
}[];
|
|
24
|
+
type: {
|
|
25
|
+
tag: "Struct";
|
|
26
|
+
value: {
|
|
27
|
+
fields: import("@dedot/codecs").Field[];
|
|
28
|
+
};
|
|
29
|
+
} | {
|
|
30
|
+
tag: "Enum";
|
|
31
|
+
value: {
|
|
32
|
+
members: {
|
|
33
|
+
name: string;
|
|
34
|
+
fields: import("@dedot/codecs").Field[];
|
|
35
|
+
index: number;
|
|
36
|
+
docs: string[];
|
|
37
|
+
}[];
|
|
38
|
+
};
|
|
39
|
+
} | {
|
|
40
|
+
tag: "Sequence";
|
|
41
|
+
value: {
|
|
42
|
+
typeParam: number;
|
|
43
|
+
};
|
|
44
|
+
} | {
|
|
45
|
+
tag: "SizedVec";
|
|
46
|
+
value: {
|
|
47
|
+
len: number;
|
|
48
|
+
typeParam: number;
|
|
49
|
+
};
|
|
50
|
+
} | {
|
|
51
|
+
tag: "Tuple";
|
|
52
|
+
value: {
|
|
53
|
+
fields: number[];
|
|
54
|
+
};
|
|
55
|
+
} | {
|
|
56
|
+
tag: "Primitive";
|
|
57
|
+
value: {
|
|
58
|
+
kind: "bool" | "char" | "str" | "u8" | "u16" | "u32" | "u64" | "u128" | "u256" | "i8" | "i16" | "i32" | "i64" | "i128" | "i256";
|
|
59
|
+
};
|
|
60
|
+
} | {
|
|
61
|
+
tag: "Compact";
|
|
62
|
+
value: {
|
|
63
|
+
typeParam: number;
|
|
64
|
+
};
|
|
65
|
+
} | {
|
|
66
|
+
tag: "BitSequence";
|
|
67
|
+
value: {
|
|
68
|
+
bitOrderType: number;
|
|
69
|
+
bitStoreType: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
docs: string[];
|
|
73
|
+
}[];
|
|
74
|
+
pallets: {
|
|
75
|
+
name: string;
|
|
76
|
+
storage: {
|
|
77
|
+
prefix: string;
|
|
78
|
+
entries: {
|
|
79
|
+
name: string;
|
|
80
|
+
modifier: string;
|
|
81
|
+
type: {
|
|
82
|
+
tag: "Plain";
|
|
83
|
+
value: {
|
|
84
|
+
valueTypeId: number;
|
|
85
|
+
};
|
|
86
|
+
} | {
|
|
87
|
+
tag: "Map";
|
|
88
|
+
value: {
|
|
89
|
+
hashers: ("identity" | "blake2_128" | "blake2_256" | "blake2_128Concat" | "twox128" | "twox256" | "twox64Concat")[];
|
|
90
|
+
keyTypeId: number;
|
|
91
|
+
valueTypeId: number;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
default: `0x${string}`;
|
|
95
|
+
docs: string[];
|
|
96
|
+
}[];
|
|
97
|
+
} | undefined;
|
|
98
|
+
calls: number | undefined;
|
|
99
|
+
event: number | undefined;
|
|
100
|
+
constants: {
|
|
101
|
+
name: string;
|
|
102
|
+
typeId: number;
|
|
103
|
+
value: `0x${string}`;
|
|
104
|
+
docs: string[];
|
|
105
|
+
}[];
|
|
106
|
+
error: number | undefined;
|
|
107
|
+
index: number;
|
|
108
|
+
docs: string[];
|
|
109
|
+
}[];
|
|
110
|
+
extrinsic: {
|
|
111
|
+
version: number;
|
|
112
|
+
addressTypeId: number;
|
|
113
|
+
callTypeId: number;
|
|
114
|
+
signatureTypeId: number;
|
|
115
|
+
extraTypeId: number;
|
|
116
|
+
signedExtensions: {
|
|
117
|
+
ident: string;
|
|
118
|
+
typeId: number;
|
|
119
|
+
additionalSigned: number;
|
|
120
|
+
}[];
|
|
121
|
+
};
|
|
122
|
+
runtimeType: number;
|
|
123
|
+
apis: {
|
|
124
|
+
name: string;
|
|
125
|
+
methods: {
|
|
126
|
+
name: string;
|
|
127
|
+
inputs: {
|
|
128
|
+
name: string;
|
|
129
|
+
typeId: number;
|
|
130
|
+
}[];
|
|
131
|
+
output: number;
|
|
132
|
+
docs: string[];
|
|
133
|
+
}[];
|
|
134
|
+
docs: string[];
|
|
135
|
+
}[];
|
|
136
|
+
outerEnums: {
|
|
137
|
+
callEnumTypeId: number;
|
|
138
|
+
eventEnumTypeId: number;
|
|
139
|
+
errorEnumTypeId: number;
|
|
140
|
+
};
|
|
141
|
+
custom: {
|
|
142
|
+
map: ReadonlyMap<string, {
|
|
143
|
+
typeId: number;
|
|
144
|
+
value: `0x${string}`;
|
|
145
|
+
}>;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
getPallet(name: string, throwErr?: boolean): PalletDefLatest;
|
|
149
|
+
abstract execute(...paths: string[]): unknown;
|
|
150
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { stringCamelCase } from '@polkadot/util';
|
|
2
|
+
/**
|
|
3
|
+
* @name Executor
|
|
4
|
+
* @description Execution abstraction for a specific action
|
|
5
|
+
*/
|
|
6
|
+
export class Executor {
|
|
7
|
+
#api;
|
|
8
|
+
#atBlockHash;
|
|
9
|
+
constructor(api, atBlockHash) {
|
|
10
|
+
this.#api = api;
|
|
11
|
+
this.#atBlockHash = atBlockHash;
|
|
12
|
+
}
|
|
13
|
+
get api() {
|
|
14
|
+
return this.#api;
|
|
15
|
+
}
|
|
16
|
+
get atBlockHash() {
|
|
17
|
+
return this.#atBlockHash;
|
|
18
|
+
}
|
|
19
|
+
get provider() {
|
|
20
|
+
return this.api.provider;
|
|
21
|
+
}
|
|
22
|
+
get registry() {
|
|
23
|
+
return this.api.registry;
|
|
24
|
+
}
|
|
25
|
+
get metadata() {
|
|
26
|
+
return this.api.metadataLatest;
|
|
27
|
+
}
|
|
28
|
+
getPallet(name, throwErr = true) {
|
|
29
|
+
const targetPallet = this.metadata.pallets.find((p) => stringCamelCase(p.name) === name);
|
|
30
|
+
if (!targetPallet && throwErr) {
|
|
31
|
+
throw new Error(`Pallet not found: ${name}`);
|
|
32
|
+
}
|
|
33
|
+
return targetPallet;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SubstrateApi } from '@dedot/chaintypes';
|
|
2
|
+
import { GenericSubstrateApi, RpcCallSpec, RpcParamSpec, GenericRpcCall } from '@dedot/types';
|
|
3
|
+
import { Executor } from './Executor';
|
|
4
|
+
/**
|
|
5
|
+
* @name RpcExecutor
|
|
6
|
+
* @description Execute a rpc based from an RPC spec,
|
|
7
|
+
* if a spec is not found we execute an arbitrary rpc method
|
|
8
|
+
*/
|
|
9
|
+
export declare class RpcExecutor<ChainApi extends GenericSubstrateApi = SubstrateApi> extends Executor<ChainApi> {
|
|
10
|
+
execute(section: string, method: string): GenericRpcCall;
|
|
11
|
+
tryDecode(callSpec: RpcCallSpec, raw: any): any;
|
|
12
|
+
tryEncode(paramSpec: RpcParamSpec, value: any): any;
|
|
13
|
+
checkRpcInputs(callSpec: RpcCallSpec, actualArgs: any[]): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { isFunction, u8aToHex } from '@polkadot/util';
|
|
2
|
+
import { findAliasRpcSpec, findRpcSpec } from '@dedot/specs';
|
|
3
|
+
import { assert, isNativeType } from '@dedot/utils';
|
|
4
|
+
import { Executor } from './Executor';
|
|
5
|
+
const isOptionalParam = (param) => {
|
|
6
|
+
return param.isOptional || param.name.startsWith('Option<');
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* @name RpcExecutor
|
|
10
|
+
* @description Execute a rpc based from an RPC spec,
|
|
11
|
+
* if a spec is not found we execute an arbitrary rpc method
|
|
12
|
+
*/
|
|
13
|
+
export class RpcExecutor extends Executor {
|
|
14
|
+
execute(section, method) {
|
|
15
|
+
const maybeRpcName = `${section}_${method}`;
|
|
16
|
+
const callSpec = findRpcSpec(maybeRpcName) || findAliasRpcSpec(maybeRpcName);
|
|
17
|
+
const rpcName = callSpec?.name || `${section}_${method}`;
|
|
18
|
+
const isSubscription = !!callSpec?.pubsub;
|
|
19
|
+
const fnRpc = async (...args) => {
|
|
20
|
+
assert(callSpec, 'Rpc spec not found');
|
|
21
|
+
this.checkRpcInputs(callSpec, args);
|
|
22
|
+
const { params } = callSpec;
|
|
23
|
+
const formattedInputs = args.map((input, index) => this.tryEncode(params[index], input));
|
|
24
|
+
const result = await this.provider.send(rpcName, formattedInputs);
|
|
25
|
+
return this.tryDecode(callSpec, result);
|
|
26
|
+
};
|
|
27
|
+
const fnSubRpc = async (...args) => {
|
|
28
|
+
assert(callSpec, 'Rpc spec not found');
|
|
29
|
+
const inArgs = args.slice();
|
|
30
|
+
const callback = inArgs.pop();
|
|
31
|
+
assert(isFunction(callback), 'A callback is required for subscription');
|
|
32
|
+
this.checkRpcInputs(callSpec, inArgs);
|
|
33
|
+
const onNewMessage = (error, result) => {
|
|
34
|
+
if (error) {
|
|
35
|
+
console.error(error);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
callback(this.tryDecode(callSpec, result));
|
|
39
|
+
};
|
|
40
|
+
const { params, pubsub } = callSpec;
|
|
41
|
+
const formattedInputs = inArgs.map((input, index) => this.tryEncode(params[index], input));
|
|
42
|
+
const [subname, subscribe, unsubcribe] = pubsub;
|
|
43
|
+
const subscription = this.provider.subscribe(subname, subscribe, formattedInputs, onNewMessage);
|
|
44
|
+
return async () => {
|
|
45
|
+
return subscription
|
|
46
|
+
.then((subscriptionId) => this.provider.unsubscribe(subname, unsubcribe, subscriptionId))
|
|
47
|
+
.catch((err) => {
|
|
48
|
+
console.error(err);
|
|
49
|
+
return false;
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
if (!callSpec) {
|
|
54
|
+
// Call arbitrary rpc method
|
|
55
|
+
return async (...args) => {
|
|
56
|
+
return await this.provider.send(rpcName, args);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const oneFnRpc = isSubscription ? fnSubRpc : fnRpc;
|
|
60
|
+
oneFnRpc.meta = callSpec;
|
|
61
|
+
return oneFnRpc;
|
|
62
|
+
}
|
|
63
|
+
tryDecode(callSpec, raw) {
|
|
64
|
+
if (raw === null) {
|
|
65
|
+
// We use `undefined` to represent Option::None in the client
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const { type, isScale } = callSpec;
|
|
69
|
+
if (isScale) {
|
|
70
|
+
return this.registry.findCodec(type).tryDecode(raw);
|
|
71
|
+
}
|
|
72
|
+
if (isNativeType(type)) {
|
|
73
|
+
return raw;
|
|
74
|
+
}
|
|
75
|
+
return raw;
|
|
76
|
+
}
|
|
77
|
+
tryEncode(paramSpec, value) {
|
|
78
|
+
const { type, isScale, isOptional } = paramSpec;
|
|
79
|
+
if (isScale) {
|
|
80
|
+
if (isOptional && (value === undefined || value === null)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const $codec = this.registry.findCodec(type);
|
|
84
|
+
return u8aToHex($codec.tryEncode(value));
|
|
85
|
+
}
|
|
86
|
+
// TODO generalize this!
|
|
87
|
+
if (value && value['toJSON']) {
|
|
88
|
+
return value.toJSON();
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
checkRpcInputs(callSpec, actualArgs) {
|
|
93
|
+
const params = callSpec.params;
|
|
94
|
+
if (params.length === actualArgs.length) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
params.forEach((param, idx) => {
|
|
98
|
+
const isRequiredParam = !isOptionalParam(param);
|
|
99
|
+
if (isRequiredParam && actualArgs[idx] === undefined) {
|
|
100
|
+
const requiredParamCount = params.filter((p) => !isOptionalParam(p)).length;
|
|
101
|
+
throw new Error(`Miss match RPC params, required ${requiredParamCount} params, current inputs length: ${actualArgs.length}`);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Executor } from './Executor';
|
|
2
|
+
import { GenericRuntimeApiMethod, GenericSubstrateApi, RuntimeApiMethodParamSpec, RuntimeApiMethodSpec } from '@dedot/types';
|
|
3
|
+
export declare const FallbackRuntimeApis: (string | number)[][];
|
|
4
|
+
export declare class RuntimeApiExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
|
|
5
|
+
#private;
|
|
6
|
+
execute(runtimeApi: string, method: string): GenericRuntimeApiMethod;
|
|
7
|
+
tryDecode(callSpec: RuntimeApiMethodSpec, raw: any): unknown;
|
|
8
|
+
tryEncode(paramSpec: RuntimeApiMethodParamSpec, value: any): Uint8Array;
|
|
9
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Executor } from './Executor';
|
|
2
|
+
import { assert, calculateRuntimeApiHash, stringSnakeCase } from '@dedot/utils';
|
|
3
|
+
import { isNumber, stringPascalCase, u8aConcat, u8aToHex } from '@polkadot/util';
|
|
4
|
+
import { findRuntimeApiMethodSpec } from '@dedot/specs';
|
|
5
|
+
export const FallbackRuntimeApis = [
|
|
6
|
+
['0x37e397fc7c91f5e4', 2], // Metadata Api v2
|
|
7
|
+
];
|
|
8
|
+
export class RuntimeApiExecutor extends Executor {
|
|
9
|
+
execute(runtimeApi, method) {
|
|
10
|
+
runtimeApi = stringPascalCase(runtimeApi);
|
|
11
|
+
method = stringSnakeCase(method);
|
|
12
|
+
const callName = `${runtimeApi}_${method}`;
|
|
13
|
+
const callSpec = this.#findRuntimeApiMethodSpec(runtimeApi, method);
|
|
14
|
+
assert(callSpec, `Runtime Api not found: ${callName}`);
|
|
15
|
+
const callFn = async (...args) => {
|
|
16
|
+
const { params } = callSpec;
|
|
17
|
+
const formattedInputs = params.map((param, index) => this.tryEncode(param, args[index]));
|
|
18
|
+
const bytes = u8aToHex(u8aConcat(...formattedInputs));
|
|
19
|
+
const callArgs = [callName, bytes];
|
|
20
|
+
if (this.atBlockHash) {
|
|
21
|
+
callArgs.push(this.atBlockHash);
|
|
22
|
+
}
|
|
23
|
+
const result = await this.api.rpc.state.call(...callArgs);
|
|
24
|
+
return this.tryDecode(callSpec, result);
|
|
25
|
+
};
|
|
26
|
+
callFn.meta = callSpec;
|
|
27
|
+
return callFn;
|
|
28
|
+
}
|
|
29
|
+
tryDecode(callSpec, raw) {
|
|
30
|
+
const { type, typeId } = callSpec;
|
|
31
|
+
if (isNumber(typeId)) {
|
|
32
|
+
return this.registry.findPortableCodec(typeId).tryDecode(raw);
|
|
33
|
+
}
|
|
34
|
+
return this.registry.findCodec(type).tryDecode(raw);
|
|
35
|
+
}
|
|
36
|
+
tryEncode(paramSpec, value) {
|
|
37
|
+
const { type, typeId } = paramSpec;
|
|
38
|
+
const $codec = isNumber(typeId) ? this.registry.findPortableCodec(typeId) : this.registry.findCodec(type);
|
|
39
|
+
return $codec.tryEncode(value);
|
|
40
|
+
}
|
|
41
|
+
#findRuntimeApiMethodSpec(runtimeApi, method) {
|
|
42
|
+
const methodDef = this.#findRuntimeApiMethodDef(runtimeApi, method);
|
|
43
|
+
if (methodDef) {
|
|
44
|
+
return this.#toMethodSpec(runtimeApi, methodDef);
|
|
45
|
+
}
|
|
46
|
+
return this.#findRuntimeApiMethodExternalSpec(runtimeApi, method);
|
|
47
|
+
}
|
|
48
|
+
#findRuntimeApiMethodDef(runtimeApi, method) {
|
|
49
|
+
try {
|
|
50
|
+
for (const api of this.metadata.apis) {
|
|
51
|
+
if (api.name !== runtimeApi)
|
|
52
|
+
continue;
|
|
53
|
+
for (const apiMethod of api.methods) {
|
|
54
|
+
if (apiMethod.name === method)
|
|
55
|
+
return apiMethod;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch { }
|
|
60
|
+
}
|
|
61
|
+
#toMethodSpec(runtimeApi, methodDef) {
|
|
62
|
+
if (!methodDef)
|
|
63
|
+
return undefined;
|
|
64
|
+
const { name, inputs, output, docs } = methodDef;
|
|
65
|
+
return {
|
|
66
|
+
docs,
|
|
67
|
+
runtimeApiName: runtimeApi,
|
|
68
|
+
methodName: name,
|
|
69
|
+
type: '', // TODO generate type name
|
|
70
|
+
typeId: output,
|
|
71
|
+
params: inputs.map(({ name, typeId }) => ({
|
|
72
|
+
name,
|
|
73
|
+
type: '', // TODO generate type name
|
|
74
|
+
typeId,
|
|
75
|
+
})),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
#findRuntimeApiMethodExternalSpec(runtimeApi, method) {
|
|
79
|
+
const targetRuntimeApiHash = calculateRuntimeApiHash(runtimeApi);
|
|
80
|
+
const runtimeApiVersions = this.api.runtimeVersion?.apis || FallbackRuntimeApis;
|
|
81
|
+
const targetRuntimeApiVersion = runtimeApiVersions
|
|
82
|
+
.find(([supportedRuntimeApiHash]) => targetRuntimeApiHash === supportedRuntimeApiHash)
|
|
83
|
+
?.at(1);
|
|
84
|
+
if (!isNumber(targetRuntimeApiVersion)) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
return findRuntimeApiMethodSpec(`${runtimeApi}_${method}`, targetRuntimeApiVersion);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SubstrateApi } from '@dedot/chaintypes';
|
|
2
|
+
import { GenericSubstrateApi } from '@dedot/types';
|
|
3
|
+
import { Executor } from './Executor';
|
|
4
|
+
/**
|
|
5
|
+
* @name StorageQueryExecutor
|
|
6
|
+
* @description Execute a query to on-chain storage
|
|
7
|
+
*/
|
|
8
|
+
export declare class StorageQueryExecutor<ChainApi extends GenericSubstrateApi = SubstrateApi> extends Executor<ChainApi> {
|
|
9
|
+
execute(pallet: string, storage: string): (...args: any[]) => Promise<any>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Executor } from './Executor';
|
|
2
|
+
import { QueryableStorage } from '../storage/QueryableStorage';
|
|
3
|
+
import { isFunction } from '@polkadot/util';
|
|
4
|
+
/**
|
|
5
|
+
* @name StorageQueryExecutor
|
|
6
|
+
* @description Execute a query to on-chain storage
|
|
7
|
+
*/
|
|
8
|
+
export class StorageQueryExecutor extends Executor {
|
|
9
|
+
execute(pallet, storage) {
|
|
10
|
+
return async (...args) => {
|
|
11
|
+
const entry = new QueryableStorage(this.registry, pallet, storage);
|
|
12
|
+
const inArgs = args.slice();
|
|
13
|
+
const lastArg = args.at(-1);
|
|
14
|
+
const callback = isFunction(lastArg) ? inArgs.pop() : undefined;
|
|
15
|
+
const encodedKey = entry.encodeKey(inArgs.at(0));
|
|
16
|
+
// if a callback is passed, make a storage subscription and return an unsub function
|
|
17
|
+
if (callback) {
|
|
18
|
+
return await this.api.rpc.state.subscribeStorage([encodedKey], (changeSet) => {
|
|
19
|
+
const targetChange = changeSet.changes.find((change) => change[0] === encodedKey);
|
|
20
|
+
targetChange && callback(entry.decodeValue(targetChange[1]));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
const result = await this.api.rpc.state.getStorage(encodedKey, this.atBlockHash);
|
|
25
|
+
return entry.decodeValue(result);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Executor } from './Executor';
|
|
2
|
+
import { AddressOrPair, Callback, DryRunResult, GenericSubstrateApi, GenericTxCall, IRuntimeTxCall, ISubmittableResult, SignerOptions, Unsub } from '@dedot/types';
|
|
3
|
+
import { SubstrateApi } from '@dedot/chaintypes';
|
|
4
|
+
import { HexString } from '@dedot/utils';
|
|
5
|
+
import { BlockHash, Hash } from '@dedot/codecs';
|
|
6
|
+
import { IKeyringPair } from '@polkadot/types/types';
|
|
7
|
+
import { SignOptions } from '@polkadot/keyring/types';
|
|
8
|
+
export declare function isKeyringPair(account: AddressOrPair): account is IKeyringPair;
|
|
9
|
+
/**
|
|
10
|
+
* Sign a raw message
|
|
11
|
+
* @param signerPair
|
|
12
|
+
* @param raw
|
|
13
|
+
* @param options
|
|
14
|
+
*/
|
|
15
|
+
export declare function signRaw(signerPair: IKeyringPair, raw: HexString, options?: SignOptions): Uint8Array;
|
|
16
|
+
/**
|
|
17
|
+
* @name TxExecutor
|
|
18
|
+
* @description Execute a transaction instruction, returns a submittable extrinsic
|
|
19
|
+
*/
|
|
20
|
+
export declare class TxExecutor<ChainApi extends GenericSubstrateApi = SubstrateApi> extends Executor<ChainApi> {
|
|
21
|
+
execute(pallet: string, functionName: string): GenericTxCall;
|
|
22
|
+
createExtrinsic(call: IRuntimeTxCall): {
|
|
23
|
+
sign(fromAccount: AddressOrPair, options?: Partial<SignerOptions>): Promise<any>;
|
|
24
|
+
signAndSend(account: AddressOrPair, options?: Partial<SignerOptions>): Promise<Hash>;
|
|
25
|
+
signAndSend(account: AddressOrPair, callback: Callback<ISubmittableResult>): Promise<Unsub>;
|
|
26
|
+
signAndSend(account: AddressOrPair, options: Partial<SignerOptions>, callback?: Callback<ISubmittableResult>): Promise<Unsub>;
|
|
27
|
+
"__#51@#normalizeOptions"(partialOptions?: Partial<SignerOptions> | Callback<ISubmittableResult>, callback?: Callback<ISubmittableResult>): [Partial<SignerOptions>, Callback<ISubmittableResult> | undefined];
|
|
28
|
+
dryRun(account: AddressOrPair, optionsOrHash?: Partial<SignerOptions> | BlockHash): Promise<DryRunResult>;
|
|
29
|
+
send(): Promise<Hash>;
|
|
30
|
+
send(callback: Callback<ISubmittableResult>): Promise<Unsub>;
|
|
31
|
+
"__#10@#private": any;
|
|
32
|
+
registry: import("@dedot/codecs").CodecRegistry;
|
|
33
|
+
readonly signed: boolean;
|
|
34
|
+
readonly version: number;
|
|
35
|
+
readonly signature: import("@dedot/codecs").ExtrinsicSignatureV4<any, any, any> | undefined;
|
|
36
|
+
readonly call: any;
|
|
37
|
+
readonly callU8a: Uint8Array;
|
|
38
|
+
readonly callHex: `0x${string}`;
|
|
39
|
+
readonly callLength: number;
|
|
40
|
+
attachSignature(signature: import("@dedot/codecs").ExtrinsicSignatureV4<any, any, any>): void;
|
|
41
|
+
readonly $Codec: import("subshape").Shape<unknown, unknown>;
|
|
42
|
+
toU8a(): Uint8Array;
|
|
43
|
+
toHex(): `0x${string}`;
|
|
44
|
+
readonly length: number;
|
|
45
|
+
readonly hash: `0x${string}`;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Executor } from './Executor';
|
|
2
|
+
import { assert } from '@dedot/utils';
|
|
3
|
+
import { hexToU8a, isFunction, isHex, objectSpread, stringCamelCase, stringPascalCase, u8aToHex } from '@polkadot/util';
|
|
4
|
+
import { Extrinsic } from '@dedot/codecs';
|
|
5
|
+
import { ExtraSignedExtension, SubmittableResult } from '../extrinsic';
|
|
6
|
+
import { blake2AsHex, blake2AsU8a } from '@polkadot/util-crypto';
|
|
7
|
+
export function isKeyringPair(account) {
|
|
8
|
+
return isFunction(account.sign);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Sign a raw message
|
|
12
|
+
* @param signerPair
|
|
13
|
+
* @param raw
|
|
14
|
+
* @param options
|
|
15
|
+
*/
|
|
16
|
+
export function signRaw(signerPair, raw, options) {
|
|
17
|
+
const u8a = hexToU8a(raw);
|
|
18
|
+
// Ref: https://github.com/paritytech/polkadot-sdk/blob/943697fa693a4da6ef481ef93df522accb7d0583/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs#L234-L238
|
|
19
|
+
const toSignRaw = u8a.length > 256 ? blake2AsU8a(u8a, 256) : u8a;
|
|
20
|
+
return signerPair.sign(toSignRaw, options);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @name TxExecutor
|
|
24
|
+
* @description Execute a transaction instruction, returns a submittable extrinsic
|
|
25
|
+
*/
|
|
26
|
+
export class TxExecutor extends Executor {
|
|
27
|
+
execute(pallet, functionName) {
|
|
28
|
+
const targetPallet = this.getPallet(pallet);
|
|
29
|
+
assert(targetPallet.calls, 'Tx call type not found');
|
|
30
|
+
const txType = this.metadata.types[targetPallet.calls];
|
|
31
|
+
assert(txType.type.tag === 'Enum', 'Tx type should be enum');
|
|
32
|
+
const isFlatEnum = txType.type.value.members.every((m) => m.fields.length === 0);
|
|
33
|
+
const txCallDef = txType.type.value.members.find((m) => stringCamelCase(m.name) === functionName);
|
|
34
|
+
assert(txCallDef, 'Tx call not found');
|
|
35
|
+
const txCallFn = (...args) => {
|
|
36
|
+
let call;
|
|
37
|
+
if (isFlatEnum) {
|
|
38
|
+
call = {
|
|
39
|
+
pallet: stringPascalCase(targetPallet.name),
|
|
40
|
+
palletCall: stringPascalCase(txCallDef.name),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const callParams = txCallDef.fields.reduce((o, { name }, idx) => {
|
|
45
|
+
o[stringCamelCase(name)] = args[idx];
|
|
46
|
+
return o;
|
|
47
|
+
}, {});
|
|
48
|
+
call = {
|
|
49
|
+
pallet: stringPascalCase(targetPallet.name),
|
|
50
|
+
palletCall: {
|
|
51
|
+
name: stringPascalCase(txCallDef.name),
|
|
52
|
+
params: callParams,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return this.createExtrinsic(call);
|
|
57
|
+
};
|
|
58
|
+
txCallFn.meta = {
|
|
59
|
+
...txCallDef,
|
|
60
|
+
fieldCodecs: txCallDef.fields.map(({ typeId }) => this.registry.findPortableCodec(typeId)),
|
|
61
|
+
pallet: targetPallet.name,
|
|
62
|
+
palletIndex: targetPallet.index,
|
|
63
|
+
};
|
|
64
|
+
return txCallFn;
|
|
65
|
+
}
|
|
66
|
+
createExtrinsic(call) {
|
|
67
|
+
const api = this.api;
|
|
68
|
+
class SubmittableExtrinsic extends Extrinsic {
|
|
69
|
+
async sign(fromAccount, options) {
|
|
70
|
+
const address = isKeyringPair(fromAccount) ? fromAccount.address : fromAccount.toString();
|
|
71
|
+
const extra = new ExtraSignedExtension(api, {
|
|
72
|
+
signerAddress: address,
|
|
73
|
+
payloadOptions: options,
|
|
74
|
+
});
|
|
75
|
+
await extra.init();
|
|
76
|
+
const { signer } = options || {};
|
|
77
|
+
let signature;
|
|
78
|
+
if (isKeyringPair(fromAccount)) {
|
|
79
|
+
signature = u8aToHex(signRaw(fromAccount, extra.toRawPayload(this.callHex).data, { withType: true }));
|
|
80
|
+
}
|
|
81
|
+
else if (signer?.signPayload) {
|
|
82
|
+
const result = await signer.signPayload(extra.toPayload(this.callHex));
|
|
83
|
+
signature = result.signature;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
throw new Error('Signer not found. Cannot sign the extrinsic!');
|
|
87
|
+
}
|
|
88
|
+
const { signatureTypeId } = this.registry.metadata.extrinsic;
|
|
89
|
+
const $Signature = this.registry.findPortableCodec(signatureTypeId);
|
|
90
|
+
this.attachSignature({
|
|
91
|
+
address: address,
|
|
92
|
+
signature: $Signature.tryDecode(signature),
|
|
93
|
+
extra: extra.data,
|
|
94
|
+
});
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
async signAndSend(fromAccount, partialOptions, maybeCallback) {
|
|
98
|
+
const [options, callback] = this.#normalizeOptions(partialOptions, maybeCallback);
|
|
99
|
+
await this.sign(fromAccount, options);
|
|
100
|
+
return this.send(callback);
|
|
101
|
+
}
|
|
102
|
+
#normalizeOptions(partialOptions, callback) {
|
|
103
|
+
if (isFunction(partialOptions)) {
|
|
104
|
+
return [{}, partialOptions];
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return [objectSpread({}, partialOptions), callback];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async dryRun(account, optionsOrHash) {
|
|
111
|
+
const dryRunFn = api.rpc.system.dryRun;
|
|
112
|
+
assert(dryRunFn.meta, 'system_dryRun RPC does not supported!');
|
|
113
|
+
if (isHex(optionsOrHash)) {
|
|
114
|
+
return dryRunFn(this.toHex(), optionsOrHash);
|
|
115
|
+
}
|
|
116
|
+
await this.sign(account, optionsOrHash);
|
|
117
|
+
return dryRunFn(this.toHex());
|
|
118
|
+
}
|
|
119
|
+
async send(callback) {
|
|
120
|
+
const isSubscription = !!callback;
|
|
121
|
+
const txHash = this.hash;
|
|
122
|
+
if (isSubscription) {
|
|
123
|
+
return api.rpc.author.submitAndWatchExtrinsic(this.toHex(), async (status) => {
|
|
124
|
+
if (status.tag === 'InBlock' || status.tag === 'Finalized') {
|
|
125
|
+
const blockHash = status.value;
|
|
126
|
+
const [signedBlock, blockEvents] = await Promise.all([
|
|
127
|
+
api.rpc.chain.getBlock(blockHash),
|
|
128
|
+
api.queryAt(blockHash).system.events(),
|
|
129
|
+
]);
|
|
130
|
+
const txIndex = signedBlock.block.extrinsics.findIndex((tx) => blake2AsHex(hexToU8a(tx)) === txHash);
|
|
131
|
+
assert(txIndex >= 0, 'Extrinsic not found!');
|
|
132
|
+
const events = blockEvents.filter(({ phase }) => phase.tag === 'ApplyExtrinsic' && phase.value === txIndex);
|
|
133
|
+
return callback(new SubmittableResult({ status, txHash, events, txIndex }));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
return callback(new SubmittableResult({ status, txHash }));
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
return api.rpc.author.submitExtrinsic(this.toHex());
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return new SubmittableExtrinsic(api.registry, call);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './Executor';
|
|
2
|
+
export * from './RpcExecutor';
|
|
3
|
+
export * from './ConstantExecutor';
|
|
4
|
+
export * from './StorageQueryExecutor';
|
|
5
|
+
export * from './ErrorExecutor';
|
|
6
|
+
export * from './EventExecutor';
|
|
7
|
+
export * from './RuntimeApiExecutor';
|
|
8
|
+
export * from './TxExecutor';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './Executor';
|
|
2
|
+
export * from './RpcExecutor';
|
|
3
|
+
export * from './ConstantExecutor';
|
|
4
|
+
export * from './StorageQueryExecutor';
|
|
5
|
+
export * from './ErrorExecutor';
|
|
6
|
+
export * from './EventExecutor';
|
|
7
|
+
export * from './RuntimeApiExecutor';
|
|
8
|
+
export * from './TxExecutor';
|