@dedot/api 0.0.1-alpha.32
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 +1 -0
- package/chaintypes/index.d.ts +1 -0
- package/chaintypes/index.js +1 -0
- package/chaintypes/substrate/consts.d.ts +2132 -0
- package/chaintypes/substrate/consts.js +2 -0
- package/chaintypes/substrate/errors.d.ts +3490 -0
- package/chaintypes/substrate/errors.js +2 -0
- package/chaintypes/substrate/events.d.ts +5187 -0
- package/chaintypes/substrate/events.js +2 -0
- package/chaintypes/substrate/index.d.ts +22 -0
- package/chaintypes/substrate/index.js +2 -0
- package/chaintypes/substrate/json-rpc.d.ts +3 -0
- package/chaintypes/substrate/json-rpc.js +2 -0
- package/chaintypes/substrate/query.d.ts +3829 -0
- package/chaintypes/substrate/query.js +2 -0
- package/chaintypes/substrate/runtime.d.ts +850 -0
- package/chaintypes/substrate/runtime.js +2 -0
- package/chaintypes/substrate/tx.d.ts +14269 -0
- package/chaintypes/substrate/tx.js +2 -0
- package/chaintypes/substrate/types.d.ts +30457 -0
- package/chaintypes/substrate/types.js +2 -0
- package/cjs/chaintypes/index.js +17 -0
- package/cjs/chaintypes/substrate/consts.js +3 -0
- package/cjs/chaintypes/substrate/errors.js +3 -0
- package/cjs/chaintypes/substrate/events.js +3 -0
- package/cjs/chaintypes/substrate/index.js +18 -0
- package/cjs/chaintypes/substrate/json-rpc.js +3 -0
- package/cjs/chaintypes/substrate/query.js +3 -0
- package/cjs/chaintypes/substrate/runtime.js +3 -0
- package/cjs/chaintypes/substrate/tx.js +3 -0
- package/cjs/chaintypes/substrate/types.js +3 -0
- package/cjs/client/BaseSubstrateClient.js +257 -0
- package/cjs/client/Dedot.js +250 -0
- package/cjs/client/DedotClient.js +186 -0
- package/cjs/client/index.js +18 -0
- package/cjs/executor/ConstantExecutor.js +20 -0
- package/cjs/executor/ErrorExecutor.js +46 -0
- package/cjs/executor/EventExecutor.js +52 -0
- package/cjs/executor/Executor.js +42 -0
- package/cjs/executor/RuntimeApiExecutor.js +121 -0
- package/cjs/executor/StorageQueryExecutor.js +115 -0
- package/cjs/executor/TxExecutor.js +55 -0
- package/cjs/executor/index.js +26 -0
- package/cjs/executor/v2/RuntimeApiExecutorV2.js +21 -0
- package/cjs/executor/v2/StorageQueryExecutorV2.js +65 -0
- package/cjs/executor/v2/TxExecutorV2.js +20 -0
- package/cjs/extrinsic/extensions/ExtraSignedExtension.js +87 -0
- package/cjs/extrinsic/extensions/SignedExtension.js +41 -0
- package/cjs/extrinsic/extensions/index.js +19 -0
- package/cjs/extrinsic/extensions/known/ChargeAssetTxPayment.js +43 -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 +90 -0
- package/cjs/extrinsic/extensions/known/CheckNonZeroSender.js +10 -0
- package/cjs/extrinsic/extensions/known/CheckNonce.js +33 -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/extrinsic/submittable/BaseSubmittableExtrinsic.js +70 -0
- package/cjs/extrinsic/submittable/SubmittableExtrinsic.js +47 -0
- package/cjs/extrinsic/submittable/SubmittableExtrinsicV2.js +204 -0
- package/cjs/extrinsic/submittable/SubmittableResult.js +33 -0
- package/cjs/extrinsic/submittable/errors.js +16 -0
- package/cjs/extrinsic/submittable/fakeSigner.js +13 -0
- package/cjs/extrinsic/submittable/index.js +20 -0
- package/cjs/extrinsic/submittable/utils.js +20 -0
- package/cjs/index.js +21 -0
- package/cjs/json-rpc/JsonRpcClient.js +165 -0
- package/cjs/json-rpc/group/ChainHead/BlockUsage.js +24 -0
- package/cjs/json-rpc/group/ChainHead/ChainHead.js +603 -0
- package/cjs/json-rpc/group/ChainHead/error.js +59 -0
- package/cjs/json-rpc/group/ChainHead/index.js +18 -0
- package/cjs/json-rpc/group/ChainSpec.js +19 -0
- package/cjs/json-rpc/group/JsonRpcGroup.js +89 -0
- package/cjs/json-rpc/group/Transaction.js +25 -0
- package/cjs/json-rpc/group/TransactionWatch.js +17 -0
- package/cjs/json-rpc/group/index.js +21 -0
- package/cjs/json-rpc/index.js +20 -0
- package/cjs/json-rpc/scaledResponses.js +15 -0
- package/cjs/json-rpc/subscriptionsInfo.js +20 -0
- package/cjs/package.json +1 -0
- package/cjs/proxychain.js +27 -0
- package/cjs/storage/QueryableStorage.js +161 -0
- package/cjs/storage/index.js +17 -0
- package/cjs/types.js +2 -0
- package/client/BaseSubstrateClient.d.ts +65 -0
- package/client/BaseSubstrateClient.js +252 -0
- package/client/Dedot.d.ts +131 -0
- package/client/Dedot.js +246 -0
- package/client/DedotClient.d.ts +61 -0
- package/client/DedotClient.js +182 -0
- package/client/index.d.ts +2 -0
- package/client/index.js +2 -0
- package/executor/ConstantExecutor.d.ts +10 -0
- package/executor/ConstantExecutor.js +16 -0
- package/executor/ErrorExecutor.d.ts +10 -0
- package/executor/ErrorExecutor.js +42 -0
- package/executor/EventExecutor.d.ts +10 -0
- package/executor/EventExecutor.js +48 -0
- package/executor/Executor.d.ts +149 -0
- package/executor/Executor.js +38 -0
- package/executor/RuntimeApiExecutor.d.ts +27 -0
- package/executor/RuntimeApiExecutor.js +117 -0
- package/executor/StorageQueryExecutor.d.ts +14 -0
- package/executor/StorageQueryExecutor.js +111 -0
- package/executor/TxExecutor.d.ts +10 -0
- package/executor/TxExecutor.js +51 -0
- package/executor/index.d.ts +10 -0
- package/executor/index.js +10 -0
- package/executor/v2/RuntimeApiExecutorV2.d.ts +14 -0
- package/executor/v2/RuntimeApiExecutorV2.js +17 -0
- package/executor/v2/StorageQueryExecutorV2.d.ts +17 -0
- package/executor/v2/StorageQueryExecutorV2.js +61 -0
- package/executor/v2/TxExecutorV2.d.ts +11 -0
- package/executor/v2/TxExecutorV2.js +16 -0
- package/extrinsic/extensions/ExtraSignedExtension.d.ts +14 -0
- package/extrinsic/extensions/ExtraSignedExtension.js +60 -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 +39 -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 +86 -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 +29 -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/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +17 -0
- package/extrinsic/submittable/BaseSubmittableExtrinsic.js +66 -0
- package/extrinsic/submittable/SubmittableExtrinsic.d.ts +12 -0
- package/extrinsic/submittable/SubmittableExtrinsic.js +43 -0
- package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +15 -0
- package/extrinsic/submittable/SubmittableExtrinsicV2.js +200 -0
- package/extrinsic/submittable/SubmittableResult.d.ts +19 -0
- package/extrinsic/submittable/SubmittableResult.js +29 -0
- package/extrinsic/submittable/errors.d.ts +11 -0
- package/extrinsic/submittable/errors.js +12 -0
- package/extrinsic/submittable/fakeSigner.d.ts +2 -0
- package/extrinsic/submittable/fakeSigner.js +10 -0
- package/extrinsic/submittable/index.d.ts +4 -0
- package/extrinsic/submittable/index.js +4 -0
- package/extrinsic/submittable/utils.d.ts +10 -0
- package/extrinsic/submittable/utils.js +15 -0
- package/index.d.ts +6 -0
- package/index.js +5 -0
- package/json-rpc/JsonRpcClient.d.ts +50 -0
- package/json-rpc/JsonRpcClient.js +160 -0
- package/json-rpc/group/ChainHead/BlockUsage.d.ts +11 -0
- package/json-rpc/group/ChainHead/BlockUsage.js +20 -0
- package/json-rpc/group/ChainHead/ChainHead.d.ts +70 -0
- package/json-rpc/group/ChainHead/ChainHead.js +599 -0
- package/json-rpc/group/ChainHead/error.d.ts +47 -0
- package/json-rpc/group/ChainHead/error.js +48 -0
- package/json-rpc/group/ChainHead/index.d.ts +2 -0
- package/json-rpc/group/ChainHead/index.js +2 -0
- package/json-rpc/group/ChainSpec.d.ts +10 -0
- package/json-rpc/group/ChainSpec.js +15 -0
- package/json-rpc/group/JsonRpcGroup.d.ts +74 -0
- package/json-rpc/group/JsonRpcGroup.js +85 -0
- package/json-rpc/group/Transaction.d.ts +11 -0
- package/json-rpc/group/Transaction.js +21 -0
- package/json-rpc/group/TransactionWatch.d.ts +10 -0
- package/json-rpc/group/TransactionWatch.js +13 -0
- package/json-rpc/group/index.d.ts +5 -0
- package/json-rpc/group/index.js +5 -0
- package/json-rpc/index.d.ts +4 -0
- package/json-rpc/index.js +4 -0
- package/json-rpc/scaledResponses.d.ts +2 -0
- package/json-rpc/scaledResponses.js +12 -0
- package/json-rpc/subscriptionsInfo.d.ts +2 -0
- package/json-rpc/subscriptionsInfo.js +17 -0
- package/package.json +54 -0
- package/proxychain.d.ts +14 -0
- package/proxychain.js +23 -0
- package/storage/QueryableStorage.d.ts +35 -0
- package/storage/QueryableStorage.js +157 -0
- package/storage/index.d.ts +1 -0
- package/storage/index.js +1 -0
- package/types.d.ts +120 -0
- package/types.js +1 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { assert, bnMin, isZeroHex, numberToHex, u8aToHex } from '@dedot/utils';
|
|
2
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
3
|
+
export const MAX_FINALITY_LAG = 5;
|
|
4
|
+
export const FALLBACK_MAX_HASH_COUNT = 250;
|
|
5
|
+
export const FALLBACK_PERIOD = 6 * 1000;
|
|
6
|
+
export const MORTAL_PERIOD = 5 * 60 * 1000;
|
|
7
|
+
/**
|
|
8
|
+
* @description Check for transaction mortality.
|
|
9
|
+
*/
|
|
10
|
+
export class CheckMortality extends SignedExtension {
|
|
11
|
+
#signingHeader;
|
|
12
|
+
async init() {
|
|
13
|
+
this.#signingHeader = await this.#getSigningHeader();
|
|
14
|
+
this.data = { period: this.#calculateMortalLength(), current: BigInt(this.#signingHeader.number) };
|
|
15
|
+
this.additionalSigned = this.#signingHeader.hash;
|
|
16
|
+
}
|
|
17
|
+
async #getSigningHeader() {
|
|
18
|
+
if (this.api.rpcVersion === 'v2') {
|
|
19
|
+
return this.#getSigningHeaderRpcV2();
|
|
20
|
+
}
|
|
21
|
+
return this.#getSigningHeaderViaLegacyRpc();
|
|
22
|
+
}
|
|
23
|
+
async #getSigningHeaderRpcV2() {
|
|
24
|
+
const api = this.api;
|
|
25
|
+
// TODO similar to the legacy, we should account for the case finality is lagging behind
|
|
26
|
+
const finalizedBlock = await api.chainHead.finalizedBlock();
|
|
27
|
+
return {
|
|
28
|
+
hash: finalizedBlock.hash,
|
|
29
|
+
number: finalizedBlock.number,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// Ref: https://github.com/polkadot-js/api/blob/3bdf49b0428a62f16b3222b9a31bfefa43c1ca55/packages/api-derive/src/tx/signingInfo.ts#L34-L64
|
|
33
|
+
async #getSigningHeaderViaLegacyRpc() {
|
|
34
|
+
const [header, finalizedHash] = await Promise.all([
|
|
35
|
+
this.api.rpc.chain_getHeader(),
|
|
36
|
+
this.api.rpc.chain_getFinalizedHead(),
|
|
37
|
+
]);
|
|
38
|
+
assert(header, 'Current header not found');
|
|
39
|
+
const [currentHeader, finalizedHeader] = await Promise.all([
|
|
40
|
+
Promise.resolve(header).then((header) => {
|
|
41
|
+
const { parentHash } = header;
|
|
42
|
+
if (parentHash.length === 0 || isZeroHex(parentHash)) {
|
|
43
|
+
return header;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
return this.api.rpc.chain_getHeader(parentHash);
|
|
47
|
+
}
|
|
48
|
+
}),
|
|
49
|
+
this.api.rpc.chain_getHeader(finalizedHash),
|
|
50
|
+
]);
|
|
51
|
+
assert(currentHeader, 'Cannot determine current header');
|
|
52
|
+
if (!finalizedHeader || currentHeader.number - finalizedHeader.number > MAX_FINALITY_LAG) {
|
|
53
|
+
return this.#toSigningHeader(currentHeader);
|
|
54
|
+
}
|
|
55
|
+
return this.#toSigningHeader(finalizedHeader);
|
|
56
|
+
}
|
|
57
|
+
async #toSigningHeader(header) {
|
|
58
|
+
const { number } = header;
|
|
59
|
+
const hash = (await this.api.rpc.chain_getBlockHash(header.number));
|
|
60
|
+
return { hash, number };
|
|
61
|
+
}
|
|
62
|
+
// Ref: https://github.com/polkadot-js/api/blob/3bdf49b0428a62f16b3222b9a31bfefa43c1ca55/packages/api-derive/src/tx/signingInfo.ts#L83-L93
|
|
63
|
+
#calculateMortalLength() {
|
|
64
|
+
return bnMin(BigInt(this.#getConst('system', 'blockHashCount') || FALLBACK_MAX_HASH_COUNT), BigInt(MORTAL_PERIOD) /
|
|
65
|
+
BigInt(this.#getConst('babe', 'expectedBlockTime') ||
|
|
66
|
+
BigInt(this.#getConst('timestamp', 'minimumPeriod') || 0) * 2n ||
|
|
67
|
+
FALLBACK_PERIOD) +
|
|
68
|
+
BigInt(MAX_FINALITY_LAG));
|
|
69
|
+
}
|
|
70
|
+
#getConst(pallet, name) {
|
|
71
|
+
try {
|
|
72
|
+
return this.api.consts[pallet][name];
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// ignore this on purpose
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
toPayload() {
|
|
80
|
+
return {
|
|
81
|
+
era: u8aToHex(this.$Data.tryEncode(this.data)),
|
|
82
|
+
blockHash: this.additionalSigned,
|
|
83
|
+
blockNumber: numberToHex(this.#signingHeader.number),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SignerPayloadJSON } from '@polkadot/types/types';
|
|
2
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
3
|
+
/**
|
|
4
|
+
* @description Nonce check and increment to give replay protection for transactions.
|
|
5
|
+
*/
|
|
6
|
+
export declare class CheckNonce extends SignedExtension<number> {
|
|
7
|
+
#private;
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
toPayload(): Partial<SignerPayloadJSON>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { assert, numberToHex } from '@dedot/utils';
|
|
2
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
3
|
+
/**
|
|
4
|
+
* @description Nonce check and increment to give replay protection for transactions.
|
|
5
|
+
*/
|
|
6
|
+
export class CheckNonce extends SignedExtension {
|
|
7
|
+
async init() {
|
|
8
|
+
this.data = this.payloadOptions.nonce || (await this.#getNonce());
|
|
9
|
+
}
|
|
10
|
+
async #getNonce() {
|
|
11
|
+
const { signerAddress } = this.options || {};
|
|
12
|
+
assert(signerAddress, 'Signer address not found');
|
|
13
|
+
try {
|
|
14
|
+
return (await this.api.query.system.account(signerAddress)).nonce;
|
|
15
|
+
}
|
|
16
|
+
catch { }
|
|
17
|
+
try {
|
|
18
|
+
return await this.api.call.accountNonceApi.accountNonce(signerAddress);
|
|
19
|
+
}
|
|
20
|
+
catch { }
|
|
21
|
+
// TODO fallback to api.rpc.system_accountNextIndex if needed
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
toPayload() {
|
|
25
|
+
return {
|
|
26
|
+
nonce: numberToHex(this.data),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SignerPayloadJSON } from '@polkadot/types/types';
|
|
2
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
3
|
+
/**
|
|
4
|
+
* @description Ensure the runtime version registered in the transaction is the same as at present.
|
|
5
|
+
*/
|
|
6
|
+
export declare class CheckSpecVersion extends SignedExtension<{}, number> {
|
|
7
|
+
init(): Promise<void>;
|
|
8
|
+
toPayload(): Partial<SignerPayloadJSON>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { numberToHex } from '@dedot/utils';
|
|
2
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
3
|
+
/**
|
|
4
|
+
* @description Ensure the runtime version registered in the transaction is the same as at present.
|
|
5
|
+
*/
|
|
6
|
+
export class CheckSpecVersion extends SignedExtension {
|
|
7
|
+
async init() {
|
|
8
|
+
this.additionalSigned = this.api.runtimeVersion.specVersion;
|
|
9
|
+
}
|
|
10
|
+
toPayload() {
|
|
11
|
+
return {
|
|
12
|
+
specVersion: numberToHex(this.additionalSigned),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SignerPayloadJSON } from '@polkadot/types/types';
|
|
2
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
3
|
+
/**
|
|
4
|
+
* @description Ensure the transaction version registered in the transaction is the same as at present.
|
|
5
|
+
*/
|
|
6
|
+
export declare class CheckTxVersion extends SignedExtension<{}, number> {
|
|
7
|
+
init(): Promise<void>;
|
|
8
|
+
toPayload(): Partial<SignerPayloadJSON>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { numberToHex } from '@dedot/utils';
|
|
2
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
3
|
+
/**
|
|
4
|
+
* @description Ensure the transaction version registered in the transaction is the same as at present.
|
|
5
|
+
*/
|
|
6
|
+
export class CheckTxVersion extends SignedExtension {
|
|
7
|
+
async init() {
|
|
8
|
+
this.additionalSigned = this.api.runtimeVersion.transactionVersion;
|
|
9
|
+
}
|
|
10
|
+
toPayload() {
|
|
11
|
+
return {
|
|
12
|
+
transactionVersion: numberToHex(this.additionalSigned),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
2
|
+
/**
|
|
3
|
+
* @description Validate `attest` calls prior to execution.
|
|
4
|
+
* Needed to avoid a DoS attack since they are otherwise free to place on chain.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PrevalidateAttests extends SignedExtension {
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SignedExtension } from '../SignedExtension.js';
|
|
2
|
+
/**
|
|
3
|
+
* @description Validate `attest` calls prior to execution.
|
|
4
|
+
* Needed to avoid a DoS attack since they are otherwise free to place on chain.
|
|
5
|
+
*/
|
|
6
|
+
export class PrevalidateAttests extends SignedExtension {
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ChargeAssetTxPayment } from './ChargeAssetTxPayment.js';
|
|
2
|
+
import { ChargeTransactionPayment } from './ChargeTransactionPayment.js';
|
|
3
|
+
import { CheckGenesis } from './CheckGenesis.js';
|
|
4
|
+
import { CheckMortality } from './CheckMortality.js';
|
|
5
|
+
import { CheckNonZeroSender } from './CheckNonZeroSender.js';
|
|
6
|
+
import { CheckNonce } from './CheckNonce.js';
|
|
7
|
+
import { CheckSpecVersion } from './CheckSpecVersion.js';
|
|
8
|
+
import { CheckTxVersion } from './CheckTxVersion.js';
|
|
9
|
+
import { CheckWeight } from './CheckWeight.js';
|
|
10
|
+
import { PrevalidateAttests } from './PrevalidateAttests.js';
|
|
11
|
+
export const knownSignedExtensions = {
|
|
12
|
+
CheckNonZeroSender,
|
|
13
|
+
CheckSpecVersion,
|
|
14
|
+
CheckTxVersion,
|
|
15
|
+
CheckGenesis,
|
|
16
|
+
CheckMortality,
|
|
17
|
+
CheckNonce,
|
|
18
|
+
CheckWeight,
|
|
19
|
+
ChargeTransactionPayment,
|
|
20
|
+
PrevalidateAttests,
|
|
21
|
+
ChargeAssetTxPayment,
|
|
22
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BlockHash, Extrinsic, Hash } from '@dedot/codecs';
|
|
2
|
+
import { AddressOrPair, Callback, IRuntimeTxCall, ISubmittableExtrinsic, ISubmittableResult, PayloadOptions, SignerOptions, TxPaymentInfo, Unsub } from '@dedot/types';
|
|
3
|
+
import type { FrameSystemEventRecord } from '../../chaintypes/index.js';
|
|
4
|
+
import type { ISubstrateClient } from '../../types.js';
|
|
5
|
+
export declare abstract class BaseSubmittableExtrinsic extends Extrinsic implements ISubmittableExtrinsic {
|
|
6
|
+
#private;
|
|
7
|
+
api: ISubstrateClient;
|
|
8
|
+
constructor(api: ISubstrateClient, call: IRuntimeTxCall);
|
|
9
|
+
paymentInfo(account: AddressOrPair, options?: Partial<PayloadOptions>): Promise<TxPaymentInfo>;
|
|
10
|
+
sign(fromAccount: AddressOrPair, options?: Partial<SignerOptions>): Promise<this>;
|
|
11
|
+
signAndSend(account: AddressOrPair, options?: Partial<SignerOptions>): Promise<Hash>;
|
|
12
|
+
signAndSend(account: AddressOrPair, callback: Callback<ISubmittableResult>): Promise<Unsub>;
|
|
13
|
+
signAndSend(account: AddressOrPair, options: Partial<SignerOptions>, callback?: Callback<ISubmittableResult>): Promise<Unsub>;
|
|
14
|
+
send(): Promise<Hash>;
|
|
15
|
+
send(callback: Callback): Promise<Unsub>;
|
|
16
|
+
protected getSystemEventsAt(hash: BlockHash): Promise<FrameSystemEventRecord[]>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Extrinsic } from '@dedot/codecs';
|
|
2
|
+
import { isFunction, u8aToHex } from '@dedot/utils';
|
|
3
|
+
import { ExtraSignedExtension } from '../extensions/index.js';
|
|
4
|
+
import { fakeSigner } from './fakeSigner.js';
|
|
5
|
+
import { isKeyringPair, signRaw } from './utils.js';
|
|
6
|
+
export class BaseSubmittableExtrinsic extends Extrinsic {
|
|
7
|
+
api;
|
|
8
|
+
constructor(api, call) {
|
|
9
|
+
super(api.registry, call);
|
|
10
|
+
this.api = api;
|
|
11
|
+
}
|
|
12
|
+
async paymentInfo(account, options) {
|
|
13
|
+
await this.sign(account, { ...options, signer: fakeSigner });
|
|
14
|
+
const txU8a = this.toU8a();
|
|
15
|
+
const api = this.api;
|
|
16
|
+
return api.call.transactionPaymentApi.queryInfo(txU8a, txU8a.length);
|
|
17
|
+
}
|
|
18
|
+
async sign(fromAccount, options) {
|
|
19
|
+
const address = isKeyringPair(fromAccount) ? fromAccount.address : fromAccount.toString();
|
|
20
|
+
const extra = new ExtraSignedExtension(this.api, {
|
|
21
|
+
signerAddress: address,
|
|
22
|
+
payloadOptions: options,
|
|
23
|
+
});
|
|
24
|
+
await extra.init();
|
|
25
|
+
const { signer } = options || {};
|
|
26
|
+
let signature;
|
|
27
|
+
if (isKeyringPair(fromAccount)) {
|
|
28
|
+
signature = u8aToHex(signRaw(fromAccount, extra.toRawPayload(this.callHex).data));
|
|
29
|
+
}
|
|
30
|
+
else if (signer?.signPayload) {
|
|
31
|
+
const result = await signer.signPayload(extra.toPayload(this.callHex));
|
|
32
|
+
signature = result.signature;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
throw new Error('Signer not found. Cannot sign the extrinsic!');
|
|
36
|
+
}
|
|
37
|
+
const { signatureTypeId } = this.registry.metadata.extrinsic;
|
|
38
|
+
const $Signature = this.registry.findCodec(signatureTypeId);
|
|
39
|
+
this.attachSignature({
|
|
40
|
+
address: address,
|
|
41
|
+
signature: $Signature.tryDecode(signature),
|
|
42
|
+
extra: extra.data,
|
|
43
|
+
});
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
async signAndSend(fromAccount, partialOptions, maybeCallback) {
|
|
47
|
+
const [options, callback] = this.#normalizeOptions(partialOptions, maybeCallback);
|
|
48
|
+
await this.sign(fromAccount, options);
|
|
49
|
+
return this.send(callback);
|
|
50
|
+
}
|
|
51
|
+
#normalizeOptions(partialOptions, callback) {
|
|
52
|
+
if (isFunction(partialOptions)) {
|
|
53
|
+
return [{}, partialOptions];
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return [Object.assign({}, partialOptions), callback];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
send(callback) {
|
|
60
|
+
throw new Error('Unimplemented!');
|
|
61
|
+
}
|
|
62
|
+
async getSystemEventsAt(hash) {
|
|
63
|
+
const atApi = (await this.api.at(hash));
|
|
64
|
+
return await atApi.query.system.events();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BlockHash, Hash } from '@dedot/codecs';
|
|
2
|
+
import { AddressOrPair, Callback, DryRunResult, ISubmittableExtrinsicLegacy, ISubmittableResult, SignerOptions, Unsub } from '@dedot/types';
|
|
3
|
+
import { BaseSubmittableExtrinsic } from './BaseSubmittableExtrinsic.js';
|
|
4
|
+
/**
|
|
5
|
+
* @name SubmittableExtrinsic
|
|
6
|
+
* @description A wrapper around an Extrinsic that exposes methods to sign, send, and other utility around Extrinsic.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SubmittableExtrinsic extends BaseSubmittableExtrinsic implements ISubmittableExtrinsicLegacy {
|
|
9
|
+
dryRun(account: AddressOrPair, optionsOrHash?: Partial<SignerOptions> | BlockHash): Promise<DryRunResult>;
|
|
10
|
+
send(): Promise<Hash>;
|
|
11
|
+
send(callback: Callback<ISubmittableResult>): Promise<Unsub>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { assert, isHex } from '@dedot/utils';
|
|
2
|
+
import { BaseSubmittableExtrinsic } from './BaseSubmittableExtrinsic.js';
|
|
3
|
+
import { SubmittableResult } from './SubmittableResult.js';
|
|
4
|
+
/**
|
|
5
|
+
* @name SubmittableExtrinsic
|
|
6
|
+
* @description A wrapper around an Extrinsic that exposes methods to sign, send, and other utility around Extrinsic.
|
|
7
|
+
*/
|
|
8
|
+
export class SubmittableExtrinsic extends BaseSubmittableExtrinsic {
|
|
9
|
+
async dryRun(account, optionsOrHash) {
|
|
10
|
+
const dryRunFn = this.api.rpc.system_dryRun;
|
|
11
|
+
if (isHex(optionsOrHash)) {
|
|
12
|
+
return dryRunFn(this.toHex(), optionsOrHash);
|
|
13
|
+
}
|
|
14
|
+
await this.sign(account, optionsOrHash);
|
|
15
|
+
return dryRunFn(this.toHex());
|
|
16
|
+
}
|
|
17
|
+
async send(callback) {
|
|
18
|
+
const isSubscription = !!callback;
|
|
19
|
+
const txHex = this.toHex();
|
|
20
|
+
const txHash = this.hash;
|
|
21
|
+
if (isSubscription) {
|
|
22
|
+
return this.api.rpc.author_submitAndWatchExtrinsic(txHex, async (status) => {
|
|
23
|
+
if (status.tag === 'InBlock' || status.tag === 'Finalized') {
|
|
24
|
+
const blockHash = status.value;
|
|
25
|
+
const [signedBlock, blockEvents] = await Promise.all([
|
|
26
|
+
this.api.rpc.chain_getBlock(blockHash),
|
|
27
|
+
this.getSystemEventsAt(blockHash),
|
|
28
|
+
]);
|
|
29
|
+
const txIndex = signedBlock.block.extrinsics.indexOf(txHex);
|
|
30
|
+
assert(txIndex >= 0, 'Extrinsic not found!');
|
|
31
|
+
const events = blockEvents.filter(({ phase }) => phase.tag === 'ApplyExtrinsic' && phase.value === txIndex);
|
|
32
|
+
return callback(new SubmittableResult({ status, txHash, events, txIndex }));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
return callback(new SubmittableResult({ status, txHash }));
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return this.api.rpc.author_submitExtrinsic(txHex);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Hash } from '@dedot/codecs';
|
|
2
|
+
import type { Callback, IRuntimeTxCall, Unsub } from '@dedot/types';
|
|
3
|
+
import { DedotClient } from '../../client/index.js';
|
|
4
|
+
import { BaseSubmittableExtrinsic } from './BaseSubmittableExtrinsic.js';
|
|
5
|
+
/**
|
|
6
|
+
* @name SubmittableExtrinsicV2
|
|
7
|
+
* @description Submittable extrinsic based on JSON-RPC v2
|
|
8
|
+
*/
|
|
9
|
+
export declare class SubmittableExtrinsicV2 extends BaseSubmittableExtrinsic {
|
|
10
|
+
#private;
|
|
11
|
+
api: DedotClient;
|
|
12
|
+
constructor(api: DedotClient, call: IRuntimeTxCall);
|
|
13
|
+
send(): Promise<Hash>;
|
|
14
|
+
send(callback: Callback): Promise<Unsub>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { AsyncQueue, deferred, noop } from '@dedot/utils';
|
|
2
|
+
import { BaseSubmittableExtrinsic } from './BaseSubmittableExtrinsic.js';
|
|
3
|
+
import { SubmittableResult } from './SubmittableResult.js';
|
|
4
|
+
import { InvalidTxError } from './errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* @name SubmittableExtrinsicV2
|
|
7
|
+
* @description Submittable extrinsic based on JSON-RPC v2
|
|
8
|
+
*/
|
|
9
|
+
export class SubmittableExtrinsicV2 extends BaseSubmittableExtrinsic {
|
|
10
|
+
api;
|
|
11
|
+
constructor(api, call) {
|
|
12
|
+
super(api, call);
|
|
13
|
+
this.api = api;
|
|
14
|
+
}
|
|
15
|
+
async #send(callback) {
|
|
16
|
+
const api = this.api;
|
|
17
|
+
const txHex = this.toHex();
|
|
18
|
+
const txHash = this.hash;
|
|
19
|
+
// validate the transaction
|
|
20
|
+
// https://github.com/paritytech/json-rpc-interface-spec/issues/55#issuecomment-1609011150
|
|
21
|
+
const finalizedHash = await this.api.chainHead.finalizedHash();
|
|
22
|
+
const validateTx = async (hash) => {
|
|
23
|
+
const apiAt = await api.at(hash);
|
|
24
|
+
return apiAt.call.taggedTransactionQueue.validateTransaction('External', txHex, hash);
|
|
25
|
+
};
|
|
26
|
+
const validation = await validateTx(finalizedHash);
|
|
27
|
+
if (validation.isOk) {
|
|
28
|
+
callback(new SubmittableResult({ status: { tag: 'Validated' }, txHash }));
|
|
29
|
+
}
|
|
30
|
+
else if (validation.isErr) {
|
|
31
|
+
throw new InvalidTxError(`Invalid Tx: ${validation.err.tag} - ${validation.err.value.tag}`, validation);
|
|
32
|
+
}
|
|
33
|
+
const checkTxIsOnChain = async (blockHash) => {
|
|
34
|
+
if (blockHash === finalizedHash)
|
|
35
|
+
return;
|
|
36
|
+
const txs = await api.chainHead.body(blockHash);
|
|
37
|
+
const txIndex = txs.indexOf(txHex);
|
|
38
|
+
if (txIndex < 0) {
|
|
39
|
+
return checkTxIsOnChain(api.chainHead.findBlock(blockHash).parent);
|
|
40
|
+
}
|
|
41
|
+
const events = await this.getSystemEventsAt(blockHash);
|
|
42
|
+
const txEvents = events.filter(({ phase }) => phase.tag == 'ApplyExtrinsic' && phase.value === txIndex);
|
|
43
|
+
return {
|
|
44
|
+
blockHash,
|
|
45
|
+
index: txIndex,
|
|
46
|
+
events: txEvents,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
let txFound;
|
|
50
|
+
let isSearching = false;
|
|
51
|
+
let searchQueue = new AsyncQueue();
|
|
52
|
+
// TODO 1. move the searching logic into a different utility
|
|
53
|
+
// 2. properly cancel the work by actually cancel the on-going operations
|
|
54
|
+
const cancelPendingSearch = () => {
|
|
55
|
+
searchQueue.clear();
|
|
56
|
+
};
|
|
57
|
+
const cancelBodySearch = () => {
|
|
58
|
+
searchQueue.cancel();
|
|
59
|
+
};
|
|
60
|
+
const startSearching = (block) => {
|
|
61
|
+
return searchQueue.enqueue(async () => {
|
|
62
|
+
const found = await checkTxIsOnChain(block.hash);
|
|
63
|
+
if (found) {
|
|
64
|
+
cancelPendingSearch();
|
|
65
|
+
}
|
|
66
|
+
return found;
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
const checkBestBlockIncluded = async (block, bestChainChanged) => {
|
|
70
|
+
if (bestChainChanged) {
|
|
71
|
+
if (isSearching) {
|
|
72
|
+
// if the best chain is changing, we cancel the current search
|
|
73
|
+
// and start searching on the current best chain
|
|
74
|
+
cancelBodySearch();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
if (txFound)
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
isSearching = true;
|
|
83
|
+
const inBlock = await startSearching(block);
|
|
84
|
+
if (!inBlock) {
|
|
85
|
+
if (txFound && bestChainChanged) {
|
|
86
|
+
txFound = undefined;
|
|
87
|
+
callback(new SubmittableResult({
|
|
88
|
+
status: { tag: 'NoLongerInBestChain' },
|
|
89
|
+
txHash,
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (txFound && bestChainChanged) {
|
|
95
|
+
if (txFound.blockHash === inBlock.blockHash)
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
txFound = inBlock;
|
|
99
|
+
const { index: txIndex, events, blockHash } = inBlock;
|
|
100
|
+
callback(new SubmittableResult({
|
|
101
|
+
status: { tag: 'BestChainBlockIncluded', value: { blockHash, txIndex } },
|
|
102
|
+
txHash,
|
|
103
|
+
events,
|
|
104
|
+
txIndex,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// ignore this!
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
if (searchQueue.size === 0 && !searchQueue.isWorking) {
|
|
112
|
+
isSearching = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
let txUnsub;
|
|
117
|
+
// This whole thing is just to make sure
|
|
118
|
+
// that we're not calling stopBroadcastFn twice
|
|
119
|
+
let stopBroadcastFn;
|
|
120
|
+
let stopped = false;
|
|
121
|
+
const stopBroadcast = () => {
|
|
122
|
+
if (stopped)
|
|
123
|
+
return;
|
|
124
|
+
if (stopBroadcastFn) {
|
|
125
|
+
stopped = true;
|
|
126
|
+
stopBroadcastFn().catch(noop);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const checkFinalizedBlockIncluded = async (block) => {
|
|
130
|
+
const inBlock = await checkTxIsOnChain(block.hash);
|
|
131
|
+
if (inBlock) {
|
|
132
|
+
const { index: txIndex, events, blockHash } = inBlock;
|
|
133
|
+
callback(new SubmittableResult({
|
|
134
|
+
status: { tag: 'Finalized', value: { blockHash, txIndex } },
|
|
135
|
+
txHash,
|
|
136
|
+
events,
|
|
137
|
+
txIndex,
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Revalidate the tx, just in-case it becomes invalid along the way
|
|
142
|
+
// Context: https://github.com/paritytech/json-rpc-interface-spec/pull/107#issuecomment-1906008814
|
|
143
|
+
const validation = await validateTx(block.hash);
|
|
144
|
+
if (validation.isOk)
|
|
145
|
+
return;
|
|
146
|
+
callback(new SubmittableResult({
|
|
147
|
+
status: {
|
|
148
|
+
tag: 'Invalid',
|
|
149
|
+
value: { error: `Invalid Tx: ${validation.err.tag} - ${validation.err.value.tag}` },
|
|
150
|
+
},
|
|
151
|
+
txHash,
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
txUnsub().catch(noop);
|
|
155
|
+
};
|
|
156
|
+
stopBroadcastFn = await api.txBroadcaster.broadcastTx(txHex);
|
|
157
|
+
callback(new SubmittableResult({
|
|
158
|
+
status: { tag: 'Broadcasting' },
|
|
159
|
+
txHash,
|
|
160
|
+
}));
|
|
161
|
+
const stopBestBlockTrackingFn = api.chainHead.on('bestBlock', checkBestBlockIncluded);
|
|
162
|
+
const stopFinalizedBlockTrackingFn = api.chainHead.on('finalizedBlock', checkFinalizedBlockIncluded);
|
|
163
|
+
const stopTracking = () => {
|
|
164
|
+
stopBestBlockTrackingFn();
|
|
165
|
+
stopFinalizedBlockTrackingFn();
|
|
166
|
+
cancelBodySearch();
|
|
167
|
+
};
|
|
168
|
+
txUnsub = async () => {
|
|
169
|
+
stopTracking();
|
|
170
|
+
stopBroadcast();
|
|
171
|
+
};
|
|
172
|
+
return txUnsub;
|
|
173
|
+
}
|
|
174
|
+
async send(callback) {
|
|
175
|
+
const isSubscription = !!callback;
|
|
176
|
+
if (isSubscription) {
|
|
177
|
+
return this.#send(callback);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const defer = deferred();
|
|
181
|
+
try {
|
|
182
|
+
// TODO handle timeout for this with the Drop status, just in-case we somehow can't find the tx in any block
|
|
183
|
+
const unsub = await this.#send(({ status, txHash }) => {
|
|
184
|
+
if (status.tag === 'BestChainBlockIncluded' || status.tag === 'Finalized') {
|
|
185
|
+
defer.resolve(txHash);
|
|
186
|
+
unsub().catch(noop);
|
|
187
|
+
}
|
|
188
|
+
else if (status.tag === 'Invalid' || status.tag === 'Drop') {
|
|
189
|
+
defer.reject(new Error(status.value.error));
|
|
190
|
+
unsub().catch(noop);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
defer.reject(e);
|
|
196
|
+
}
|
|
197
|
+
return defer.promise;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DispatchError, DispatchInfo, Hash } from '@dedot/codecs';
|
|
2
|
+
import type { IEventRecord, ISubmittableResult } from '@dedot/types';
|
|
3
|
+
import type { FrameSystemEventRecord } from '../../chaintypes/index.js';
|
|
4
|
+
export interface SubmittableResultInputs<E extends IEventRecord = FrameSystemEventRecord, TxStatus extends any = any> {
|
|
5
|
+
events?: E[];
|
|
6
|
+
status: TxStatus;
|
|
7
|
+
txHash: Hash;
|
|
8
|
+
txIndex?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class SubmittableResult<E extends IEventRecord = FrameSystemEventRecord, TxStatus extends any = any> implements ISubmittableResult<E, TxStatus> {
|
|
11
|
+
status: TxStatus;
|
|
12
|
+
events: E[];
|
|
13
|
+
dispatchInfo?: DispatchInfo;
|
|
14
|
+
dispatchError?: DispatchError;
|
|
15
|
+
txHash: Hash;
|
|
16
|
+
txIndex?: number;
|
|
17
|
+
constructor({ events, status, txHash, txIndex }: SubmittableResultInputs<E, TxStatus>);
|
|
18
|
+
_extractDispatchInfo(): [DispatchInfo | undefined, DispatchError | undefined];
|
|
19
|
+
}
|