From cf1c2ea58ae200520bcd578f1c62c9aa06b9eca2 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 27 Oct 2020 15:19:00 +0100 Subject: [PATCH] Add dryRun capability on Submittable (#2793) * Add dryRun capability on Submittable * Ordering, add to interface * Dry run with signing * Revert paymentInfo changes --- packages/api/src/submittable/createClass.ts | 63 +++++++++++++-------- packages/api/src/submittable/types.ts | 25 +++++--- 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/packages/api/src/submittable/createClass.ts b/packages/api/src/submittable/createClass.ts index 4f08d3daef..0cb772e942 100644 --- a/packages/api/src/submittable/createClass.ts +++ b/packages/api/src/submittable/createClass.ts @@ -3,10 +3,10 @@ /* eslint-disable no-dupe-class-members */ -import { Address, Call, Extrinsic, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index, RuntimeDispatchInfo } from '@polkadot/types/interfaces'; +import { Address, ApplyExtrinsicResult, Call, Extrinsic, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index, RuntimeDispatchInfo } from '@polkadot/types/interfaces'; import { Callback, Codec, Constructor, IKeyringPair, Registry, SignatureOptions, ISubmittableResult } from '@polkadot/types/types'; import { ApiInterfaceRx, ApiTypes, SignerResult } from '../types'; -import { AddressOrPair, SignerOptions, SubmittableExtrinsic, SubmittablePaymentResult, SubmittableResultResult, SubmittableResultSubscription, SubmittableThis } from './types'; +import { AddressOrPair, SignerOptions, SubmittableExtrinsic, SubmittableDryRunResult, SubmittablePaymentResult, SubmittableResultResult, SubmittableResultSubscription, SubmittableThis } from './types'; import { Observable, of } from 'rxjs'; import { first, map, mapTo, mergeMap, switchMap, tap } from 'rxjs/operators'; @@ -39,11 +39,22 @@ export default function createClass ({ api, apiType, this.#ignoreStatusCb = apiType === 'rxjs'; } - // adds a transform to the result, applied before result is returned - withResultTransform (transform: (input: ISubmittableResult) => ISubmittableResult): this { - this.#transformResult = transform; + // dry run an extrinsic + public dryRun (account: AddressOrPair, optionsOrHash?: Partial | Uint8Array | string): SubmittableDryRunResult { + if (isString(optionsOrHash) || isU8a(optionsOrHash)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return decorateMethod( + () => api.rpc.system.dryRun(this.toHex(), optionsOrHash) + ); + } - return this; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call + return decorateMethod( + (): Observable => + this.#observeSign(account, optionsOrHash).pipe( + switchMap(() => api.rpc.system.dryRun(this.toHex())) + ) + )(); } // calculate the payment info for this transaction (if signed and submitted) @@ -51,7 +62,7 @@ export default function createClass ({ api, apiType, if (isString(optionsOrHash) || isU8a(optionsOrHash)) { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return decorateMethod( - () => api.rpc.payment.queryInfo(this.toHex(), optionsOrHash) + (): Observable => api.rpc.payment.queryInfo(this.toHex(), optionsOrHash) ); } @@ -68,7 +79,6 @@ export default function createClass ({ api, apiType, const eraOptions = this.#makeEraOptions(allOptions, signingInfo); const signOptions = this.#makeSignOptions(eraOptions, {}); - // add a fake signature to the extrinsic this.signFake(address, signOptions); return api.rpc.payment.queryInfo(this.toHex()); @@ -77,6 +87,24 @@ export default function createClass ({ api, apiType, )(); } + // send with an immediate Hash result + public send (): SubmittableResultResult; + + // send with a status callback + public send (statusCb: Callback): SubmittableResultSubscription; + + // send implementation for both immediate Hash and statusCb variants + public send (statusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { + const isSubscription = api.hasSubscriptions && (this.#ignoreStatusCb || !!statusCb); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call + return decorateMethod( + isSubscription + ? this.#observeSubscribe + : this.#observeSend + )(statusCb); + } + /** * @description Sign a transaction, returning the this to allow chaining, i.e. .sign(...).send(). When options, e.g. nonce/blockHash are not specified, it will be inferred. To retrieve eg. nonce use `signAsync` (the preferred interface, this is provided for backwards compatibility) * @deprecated @@ -125,22 +153,11 @@ export default function createClass ({ api, apiType, )(statusCb); } - // send with an immediate Hash result - public send (): SubmittableResultResult; + // adds a transform to the result, applied before result is returned + withResultTransform (transform: (input: ISubmittableResult) => ISubmittableResult): this { + this.#transformResult = transform; - // send with a status callback - public send (statusCb: Callback): SubmittableResultSubscription; - - // send implementation for both immediate Hash and statusCb variants - public send (statusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { - const isSubscription = api.hasSubscriptions && (this.#ignoreStatusCb || !!statusCb); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call - return decorateMethod( - isSubscription - ? this.#observeSubscribe - : this.#observeSend - )(statusCb); + return this; } #makeEraOptions = (options: Partial, { header, mortalLength, nonce }: { header: Header | null; mortalLength: number; nonce: Index }): SignatureOptions => { diff --git a/packages/api/src/submittable/types.ts b/packages/api/src/submittable/types.ts index a242fd6e0f..cd9f86c6ed 100644 --- a/packages/api/src/submittable/types.ts +++ b/packages/api/src/submittable/types.ts @@ -1,7 +1,7 @@ // Copyright 2017-2020 @polkadot/api authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { AccountId, Address, DispatchError, DispatchInfo, Extrinsic, ExtrinsicStatus, EventRecord, Hash, RuntimeDispatchInfo } from '@polkadot/types/interfaces'; +import { AccountId, Address, ApplyExtrinsicResult, DispatchError, DispatchInfo, Extrinsic, ExtrinsicStatus, EventRecord, Hash, RuntimeDispatchInfo } from '@polkadot/types/interfaces'; import { AnyNumber, Callback, Codec, IExtrinsicEra, IKeyringPair, Signer, ISubmittableResult } from '@polkadot/types/types'; import { ApiTypes } from '../types'; @@ -14,6 +14,11 @@ export interface SubmittableResultValue { status: ExtrinsicStatus; } +export type SubmittableDryRunResult = + ApiType extends 'rxjs' + ? Observable + : Promise; + export type SubmittablePaymentResult = ApiType extends 'rxjs' ? Observable @@ -45,24 +50,26 @@ export interface SignerOptions { export type AddressOrPair = IKeyringPair | string | AccountId | Address; export interface SubmittableExtrinsic extends Extrinsic { - withResultTransform (transform: (input: ISubmittableResult) => ISubmittableResult): this; + dryRun (account: AddressOrPair, options?: Partial): SubmittableDryRunResult; paymentInfo (account: AddressOrPair, options?: Partial): SubmittablePaymentResult; - send(): SubmittableResultResult; + send (): SubmittableResultResult; - send(statusCb: Callback): SubmittableResultSubscription; + send (statusCb: Callback): SubmittableResultSubscription; /** * @deprecated */ - sign(account: IKeyringPair, _options?: Partial): this; + sign (account: IKeyringPair, _options?: Partial): this; - signAsync(account: AddressOrPair, _options?: Partial): SubmittableThis; + signAsync (account: AddressOrPair, _options?: Partial): SubmittableThis; - signAndSend(account: AddressOrPair, options?: Partial): SubmittableResultResult; + signAndSend (account: AddressOrPair, options?: Partial): SubmittableResultResult; - signAndSend(account: AddressOrPair, statusCb: Callback): SubmittableResultSubscription; + signAndSend (account: AddressOrPair, statusCb: Callback): SubmittableResultSubscription; - signAndSend(account: AddressOrPair, options: Partial, statusCb?: Callback): SubmittableResultSubscription; + signAndSend (account: AddressOrPair, options: Partial, statusCb?: Callback): SubmittableResultSubscription; + + withResultTransform (transform: (input: ISubmittableResult) => ISubmittableResult): this; }