From 4aab0cc73507f41a2f75d18eebc4572132cc9c03 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 21 Aug 2019 15:24:11 +0200 Subject: [PATCH 1/6] Minor API-related readability cleanups --- packages/api/src/SignerPayload.ts | 9 +-- packages/api/src/SubmittableExtrinsic.ts | 35 ++++----- packages/api/src/promise/Api.ts | 90 +++++++++++++++--------- packages/api/src/rx/Api.ts | 6 +- packages/types/src/primitive/Type.ts | 87 +++++++++++------------ 5 files changed, 114 insertions(+), 113 deletions(-) diff --git a/packages/api/src/SignerPayload.ts b/packages/api/src/SignerPayload.ts index c86eeb98c5a2..4c70c3c51e09 100644 --- a/packages/api/src/SignerPayload.ts +++ b/packages/api/src/SignerPayload.ts @@ -37,18 +37,11 @@ const _Payload: Constructor = Struct.with({ }); export default class Payload extends _Payload { - /** - * @description Returns this as a SignerPayloadType. This works since the Struct.with injects all the getters automatically (just ensure the 2 definitiona are matching) - */ - public get self (): SignerPayloadType { - return this as any as SignerPayloadType; - } - /** * @description Creates an representation of the structure as an ISignerPayload JSON */ public toPayload (): SignerPayload { - const { address, blockHash, blockNumber, era, genesisHash, method, nonce, runtimeVersion: { specVersion }, tip, version } = this.self; + const { address, blockHash, blockNumber, era, genesisHash, method, nonce, runtimeVersion: { specVersion }, tip, version } = this; return { address: address.toString(), diff --git a/packages/api/src/SubmittableExtrinsic.ts b/packages/api/src/SubmittableExtrinsic.ts index cd2284b1cd75..1179df778130 100644 --- a/packages/api/src/SubmittableExtrinsic.ts +++ b/packages/api/src/SubmittableExtrinsic.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AccountId, Address, Call, ExtrinsicEra, ExtrinsicStatus, EventRecord, Hash, Header, Index, SignedBlock } from '@polkadot/types/interfaces'; +import { AccountId, Address, Call, ExtrinsicEra, ExtrinsicStatus, EventRecord, Hash, Header, Index } from '@polkadot/types/interfaces'; import { AnyNumber, AnyU8a, Callback, Codec, IExtrinsic, IExtrinsicEra, IKeyringPair, SignatureOptions } from '@polkadot/types/types'; import { ApiInterfaceRx, ApiTypes } from './types'; @@ -110,8 +110,7 @@ export default function createSubmittableExtrinsic ( type: ApiTypes, api: ApiInterfaceRx, decorateMethod: ApiBase['decorateMethod'], - extrinsic: Call | Uint8Array | string, - trackingCb?: Callback + extrinsic: Call | Uint8Array | string ): SubmittableExtrinsic { const _extrinsic = createType('Extrinsic', extrinsic, { version: api.extrinsicType }) as unknown as SubmittableExtrinsic; const _noStatusCb = type === 'rxjs'; @@ -124,35 +123,27 @@ export default function createSubmittableExtrinsic ( function statusObservable (status: ExtrinsicStatus): Observable { if (!status.isFinalized) { - const result = new SubmittableResult({ status }); - - trackingCb && trackingCb(result); - - return of(result); + return of(new SubmittableResult({ status })); } const blockHash = status.asFinalized; return combineLatest([ - api.rpc.chain.getBlock(blockHash) as Observable, + api.rpc.chain.getBlock(blockHash), api.query.system.events.at(blockHash) as Observable> ]).pipe( - map(([signedBlock, allEvents]): SubmittableResult => { - const result = new SubmittableResult({ + map(([signedBlock, allEvents]): SubmittableResult => + new SubmittableResult({ events: filterEvents(_extrinsic.hash, signedBlock, allEvents), status - }); - - trackingCb && trackingCb(result); - - return result; - }) + }) + ) ); } function sendObservable (updateId: number = -1): Observable { - return (api.rpc.author - .submitExtrinsic(_extrinsic) as Observable) + return api.rpc.author + .submitExtrinsic(_extrinsic) .pipe( tap((hash): void => { updateSigner(updateId, hash); @@ -161,8 +152,8 @@ export default function createSubmittableExtrinsic ( } function subscribeObservable (updateId: number = -1): Observable { - return (api.rpc.author - .submitAndWatchExtrinsic(_extrinsic) as Observable) + return api.rpc.author + .submitAndWatchExtrinsic(_extrinsic) .pipe( switchMap((status): Observable => statusObservable(status) @@ -255,7 +246,7 @@ export default function createSubmittableExtrinsic ( // if we have an era provided already or eraLength is <= 0 (immortal) // don't get the latest block, just pass null, handle in mergeMap (isUndefined(options.era) || (isNumber(options.era) && options.era > 0)) - ? api.rpc.chain.getHeader() as Observable
+ ? api.rpc.chain.getHeader() : of(null) ]) ).pipe( diff --git a/packages/api/src/promise/Api.ts b/packages/api/src/promise/Api.ts index 4b16e389b629..52518db63283 100644 --- a/packages/api/src/promise/Api.ts +++ b/packages/api/src/promise/Api.ts @@ -12,13 +12,57 @@ import { isFunction, assert } from '@polkadot/util'; import ApiBase from '../base'; import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator'; +interface Tracker { + reject: (value: Error) => Observable; + resolve: (value: () => void) => void; +} + +// extract the arguments and callback parats from a value array possibly containing a callback +function extractArgs (args: any[], needsCallback: boolean): [any[], Callback | undefined] { + let callback: Callback | undefined; + const actualArgs = args.slice(); + + // If the last arg is a function, we pop it, put it into callback. + // actualArgs will then hold the actual arguments to be passed to `method` + if (args.length && isFunction(args[args.length - 1])) { + callback = actualArgs.pop(); + } + + // When we need a subscription, ensure that a valid callback is actually passed + assert(!needsCallback || isFunction(callback), 'Expected a callback to be passed with subscriptions'); + + return [actualArgs, callback]; +} + +// a Promise completion tracker, wrapping a complete variable that only fires once +function promiseTracker (resolve: (value: () => void) => void, reject: (value: Error) => void): Tracker { + let isCompleted = false; + const complete = (fn: Function, value: any): void => { + if (!isCompleted) { + isCompleted = true; + + fn(value); + } + }; + + return { + reject: (error: Error): Observable => { + complete(reject, error); + + return EMPTY; + }, + resolve: (value: any): void => { + complete(resolve, value); + } + }; +} + /** * # @polkadot/api/promise * * ## Overview * * @name ApiPromise - * * @description * ApiPromise is a standard JavaScript wrapper around the RPC and interfaces on the Polkadot network. As a full Promise-based, all interface calls return Promises, including the static `.create(...)`. Subscription calls utilise `(value) => {}` callbacks to pass through the latest values. * @@ -102,10 +146,8 @@ export default class ApiPromise extends ApiBase<'promise'> { /** * @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance. - * * @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or a * provider (see the constructor arguments) - * * @example *
* @@ -125,10 +167,8 @@ export default class ApiPromise extends ApiBase<'promise'> { /** * @description Creates an instance of the ApiPromise class - * * @param options Options to create an instance. This can be either [[ApiOptions]] or * an [[WsProvider]]. - * * @example *
* @@ -197,49 +237,31 @@ export default class ApiPromise extends ApiBase<'promise'> { }; } + /** + * @description Decores the method for the ApiPromise, where the results are converted to the Promise equivalent + */ protected decorateMethod (method: Method, options?: DecorateMethodOptions): StorageEntryPromiseOverloads { const needsCallback = options && options.methodName && options.methodName.includes('subscribe'); return function (...args: any[]): Promise>> | UnsubscribePromise { - let callback: Callback | undefined; - const actualArgs = args.slice(); - - // If the last arg is a function, we pop it, put it into callback. - // actualArgs will then hold the actual arguments to be passed to `method` - if (args.length && isFunction(args[args.length - 1])) { - callback = actualArgs.pop(); - } - - // When we need a subscription, ensure that a valid callback is actually passed - assert(!needsCallback || isFunction(callback), 'Expected a callback to be passed with subscriptions'); + const [actualArgs, callback] = extractArgs(args, !!needsCallback); if (!callback) { return method(...actualArgs).pipe(first()).toPromise() as Promise>>; } return new Promise((resolve, reject): void => { - let isCompleted = false; + const tracker = promiseTracker(resolve, reject); const subscription = method(...actualArgs) .pipe( // if we find an error (invalid params, etc), reject the promise - catchError((error): Observable => { - if (!isCompleted) { - isCompleted = true; - - reject(error); - } - - // we don't want to continue, so empty observable it is - return EMPTY; - }), + catchError((error): Observable => + tracker.reject(error) + ), // upon the first result, resolve the with the unsub function - tap((): void => { - if (!isCompleted) { - isCompleted = true; - - resolve((): void => subscription.unsubscribe()); - } - }) + tap((): void => + tracker.resolve((): void => subscription.unsubscribe()) + ) ) .subscribe(callback); }) as UnsubscribePromise; diff --git a/packages/api/src/rx/Api.ts b/packages/api/src/rx/Api.ts index 253ae6c537d3..6181c758fe0c 100644 --- a/packages/api/src/rx/Api.ts +++ b/packages/api/src/rx/Api.ts @@ -114,9 +114,7 @@ export default class ApiRx extends ApiBase<'rxjs'> { /** * @description Creates an ApiRx instance using the supplied provider. Returns an Observable containing the actual Api instance. - * * @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or [[WsProvider]] - * * @example *
* @@ -140,9 +138,7 @@ export default class ApiRx extends ApiBase<'rxjs'> { /** * @description Create an instance of the ApiRx class - * * @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]] - * * @example *
* @@ -164,7 +160,7 @@ export default class ApiRx extends ApiBase<'rxjs'> { super(options, 'rxjs'); this._isReadyRx = from( - // convinced you can observable from an event, however my mind groks this form better + // You can create an observable from an event, however my mind groks this form better new Promise((resolve): void => { super.on('ready', (): void => { resolve(this); diff --git a/packages/types/src/primitive/Type.ts b/packages/types/src/primitive/Type.ts index 3b7504622478..472472616160 100644 --- a/packages/types/src/primitive/Type.ts +++ b/packages/types/src/primitive/Type.ts @@ -7,7 +7,6 @@ import Text from './Text'; type Mapper = (value: string) => string; const ALLOWED_BOXES = ['Compact', 'Option', 'Vec']; - /** * @name Type * @description @@ -18,6 +17,47 @@ const ALLOWED_BOXES = ['Compact', 'Option', 'Vec']; export default class Type extends Text { private _originalLength: number; + private static _mappings: Mapper[] = [ + // alias ::Inherent -> InherentOfflineReport + Type._alias('::Inherent', 'InherentOfflineReport'), + // alias TreasuryProposal from Proposal> + Type._alias('Proposal>', 'TreasuryProposal'), + // + Type._cleanupCompact(), + // Remove all the trait prefixes + Type._removeTraits(), + // remove PairOf -> (T, T) + Type._removePairOf(), + // remove boxing, `Box` -> `Proposal` + Type._removeWrap('Box'), + // remove generics, `MisbehaviorReport` -> `MisbehaviorReport` + Type._removeGenerics(), + // alias String -> Text (compat with jsonrpc methods) + Type._alias('String', 'Text'), + // alias () -> Null + Type._alias('\\(\\)', 'Null'), + // alias Vec -> Bytes + Type._alias('Vec', 'Bytes'), + // alias &[u8] -> Bytes + Type._alias('&\\[u8\\]', 'Bytes'), + // alias RawAddress -> Address + Type._alias('RawAddress', 'Address'), + // alias Lookup::Source to Address (_could_ be AccountId on certain chains) + Type._alias('Lookup::Source', 'Address'), + // alias Lookup::Target to AccountId (always the case) + Type._alias('Lookup::Target', 'AccountId'), + // alias for grandpa, as used in polkadot + Type._alias('grandpa::AuthorityId', 'AuthorityId'), + // specific for SessionIndex (could make this session::, but be conservative) + Type._alias('session::SessionIndex', 'SessionIndex'), + // HACK duplication between contracts & primitives, however contracts prefixed with exec + Type._alias('exec::StorageKey', 'ContractStorageKey'), + // flattens tuples with one value, `(AccountId)` -> `AccountId` + Type._flattenSingleTuple(), + // converts ::Type to Type, >::Proposal -> ::Proposal + Type._removeColonPrefix() + ]; + public constructor (value: Text | Uint8Array | string = '') { // First decode it with Text const textValue = new Text(value); @@ -34,48 +74,7 @@ export default class Type extends Text { } private static decodeType (value: string): string { - const mappings: Mapper[] = [ - // alias ::Inherent -> InherentOfflineReport - Type._alias('::Inherent', 'InherentOfflineReport'), - // alias TreasuryProposal from Proposal> - Type._alias('Proposal>', 'TreasuryProposal'), - // - Type._cleanupCompact(), - // Remove all the trait prefixes - Type._removeTraits(), - // remove PairOf -> (T, T) - Type._removePairOf(), - // remove boxing, `Box` -> `Proposal` - Type._removeWrap('Box'), - // remove generics, `MisbehaviorReport` -> `MisbehaviorReport` - Type._removeGenerics(), - // alias String -> Text (compat with jsonrpc methods) - Type._alias('String', 'Text'), - // alias () -> Null - Type._alias('\\(\\)', 'Null'), - // alias Vec -> Bytes - Type._alias('Vec', 'Bytes'), - // alias &[u8] -> Bytes - Type._alias('&\\[u8\\]', 'Bytes'), - // alias RawAddress -> Address - Type._alias('RawAddress', 'Address'), - // alias Lookup::Source to Address (_could_ be AccountId on certain chains) - Type._alias('Lookup::Source', 'Address'), - // alias Lookup::Target to AccountId (always the case) - Type._alias('Lookup::Target', 'AccountId'), - // alias for grandpa, as used in polkadot - Type._alias('grandpa::AuthorityId', 'AuthorityId'), - // specific for SessionIndex (could make this session::, but be conservative) - Type._alias('session::SessionIndex', 'SessionIndex'), - // HACK duplication between contracts & primitives, however contracts prefixed with exec - Type._alias('exec::StorageKey', 'ContractStorageKey'), - // flattens tuples with one value, `(AccountId)` -> `AccountId` - Type._flattenSingleTuple(), - // converts ::Type to Type, >::Proposal -> ::Proposal - Type._removeColonPrefix() - ]; - - return mappings.reduce((result, fn): string => { + return Type._mappings.reduce((result, fn): string => { return fn(result); }, value).trim(); } @@ -121,7 +120,7 @@ export default class Type extends Text { throw new Error(`Unable to find closing matching <> on '${value}' (start ${start})`); } - private static _alias (src: string, dest: string): Mapper { + protected static _alias (src: string, dest: string): Mapper { return (value: string): string => { return value.replace( new RegExp(src, 'g'), dest From 2dab5b266ee22702084ba28ba06625ed7048d0b3 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 21 Aug 2019 15:26:29 +0200 Subject: [PATCH 2/6] Oops, remove trial protected, revert to private --- packages/types/src/primitive/Type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/primitive/Type.ts b/packages/types/src/primitive/Type.ts index 472472616160..d5756659b586 100644 --- a/packages/types/src/primitive/Type.ts +++ b/packages/types/src/primitive/Type.ts @@ -120,7 +120,7 @@ export default class Type extends Text { throw new Error(`Unable to find closing matching <> on '${value}' (start ${start})`); } - protected static _alias (src: string, dest: string): Mapper { + private static _alias (src: string, dest: string): Mapper { return (value: string): string => { return value.replace( new RegExp(src, 'g'), dest From 89169462bd63946c1bcb384d36d3b81698b61b6b Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 21 Aug 2019 15:41:04 +0200 Subject: [PATCH 3/6] Unneeded as Observable<...> casts --- packages/api/src/SubmittableExtrinsic.ts | 4 ++-- packages/api/test/e2e/api/rx-queries.spec.ts | 3 ++- packages/api/test/e2e/api/rx-tx.spec.ts | 16 +++++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/api/src/SubmittableExtrinsic.ts b/packages/api/src/SubmittableExtrinsic.ts index 1179df778130..6cf37a146133 100644 --- a/packages/api/src/SubmittableExtrinsic.ts +++ b/packages/api/src/SubmittableExtrinsic.ts @@ -246,7 +246,7 @@ export default function createSubmittableExtrinsic ( // if we have an era provided already or eraLength is <= 0 (immortal) // don't get the latest block, just pass null, handle in mergeMap (isUndefined(options.era) || (isNumber(options.era) && options.era > 0)) - ? api.rpc.chain.getHeader() + ? api.rpc.chain.getHeader() as Observable
: of(null) ]) ).pipe( @@ -301,7 +301,7 @@ export default function createSubmittableExtrinsic ( ? subscribeObservable(updateId) : sendObservable(updateId); }) - ) as Observable) + ) as Observable) // FIXME This is wrong, SubmittableResult is _not_ a codec )(statusCb); } } diff --git a/packages/api/test/e2e/api/rx-queries.spec.ts b/packages/api/test/e2e/api/rx-queries.spec.ts index 13e273aa81d8..9d2d9260eb0f 100644 --- a/packages/api/test/e2e/api/rx-queries.spec.ts +++ b/packages/api/test/e2e/api/rx-queries.spec.ts @@ -31,7 +31,8 @@ describeE2E()('Rx e2e queries', (wsUrl: string): void => { }); it('makes a query at a specific block', (done): void => { - (api.rpc.chain.getHeader() as Observable
) + api.rpc.chain + .getHeader() .pipe( switchMap(({ hash }: Header): Observable => api.query.system.events.at(hash) diff --git a/packages/api/test/e2e/api/rx-tx.spec.ts b/packages/api/test/e2e/api/rx-tx.spec.ts index 9595f6580005..d430454a90a1 100644 --- a/packages/api/test/e2e/api/rx-tx.spec.ts +++ b/packages/api/test/e2e/api/rx-tx.spec.ts @@ -27,7 +27,8 @@ describeE2E()('Rx e2e transactions', (wsUrl: string): void => { }); it('makes a transfer', (done): void => { - (api.query.system.accountNonce(keyring.bob_stash.address) as Observable) + api.query.system + .accountNonce(keyring.bob_stash.address) .pipe( first(), switchMap((nonce: Index): Observable => @@ -46,16 +47,17 @@ describeE2E()('Rx e2e transactions', (wsUrl: string): void => { it('makes a proposal', (done): void => { const amount = calculateAccountDeposit(api); - (api.query.system.accountNonce(keyring.bob_stash.address) as Observable) + const proposal = api.tx.system && api.tx.system.setCode + ? api.tx.system.setCode(randomAsHex2097152) // since impl_version 94 https://github.com/paritytech/substrate/pull/2802 + : api.tx.consensus.setCode(randomAsHex(4096)); // impl_version 0 - 93 + + api.query.system + .accountNonce(keyring.bob_stash.address) .pipe( first(), switchMap((nonce: Index): Observable => api.tx.democracy - .propose( - api.tx.system && api.tx.system.setCode - ? api.tx.system.setCode(randomAsHex2097152) // since impl_version 94 https://github.com/paritytech/substrate/pull/2802 - : api.tx.consensus.setCode(randomAsHex(4096)) // impl_version 0 - 93 - , amount) + .propose(proposal, amount) .sign(keyring.bob_stash, { nonce }) .send() ) From 7063fbce410729c9ba969c4ab0d044efbcf96dbe Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 21 Aug 2019 15:59:10 +0200 Subject: [PATCH 4/6] Split state retrieval from signAndSend --- packages/api/src/SubmittableExtrinsic.ts | 125 ++++++++++++----------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/packages/api/src/SubmittableExtrinsic.ts b/packages/api/src/SubmittableExtrinsic.ts index 6cf37a146133..b9639126800c 100644 --- a/packages/api/src/SubmittableExtrinsic.ts +++ b/packages/api/src/SubmittableExtrinsic.ts @@ -197,6 +197,20 @@ export default function createSubmittableExtrinsic ( }); } + function getPrelimState (address: string, options: Partial): Observable<[Index, Header | null]> { + return combineLatest([ + // if we have a nonce already, don't retrieve the latest, use what is there + isUndefined(options.nonce) + ? api.query.system.accountNonce(address) + : of(createType('Index', options.nonce)), + // if we have an era provided already or eraLength is <= 0 (immortal) + // don't get the latest block, just pass null, handle in mergeMap + (isUndefined(options.era) || (isNumber(options.era) && options.era > 0)) + ? api.rpc.chain.getHeader() + : of(null) + ]); + } + const signOrigin = _extrinsic.sign; Object.defineProperties( @@ -237,71 +251,60 @@ export default function createSubmittableExtrinsic ( let updateId: number | undefined; return decorateMethod( - (): Observable => (( - combineLatest([ - // if we have a nonce already, don't retrieve the latest, use what is there - isUndefined(options.nonce) - ? api.query.system.accountNonce(address) - : of(createType('Index', options.nonce)), - // if we have an era provided already or eraLength is <= 0 (immortal) - // don't get the latest block, just pass null, handle in mergeMap - (isUndefined(options.era) || (isNumber(options.era) && options.era > 0)) - ? api.rpc.chain.getHeader() as Observable
- : of(null) - ]) - ).pipe( - first(), - mergeMap(async ([nonce, header]): Promise => { - const eraOptions = expandEraOptions(options, { header, nonce }); - - // FIXME This is becoming real messy with all the options - way past - // "a method should fit on a single screen" stage. (Probably want to - // clean this when we remove `api.signer.sign` in the next beta cycle) - if (isKeyringPair(account)) { - this.sign(account, eraOptions); - } else if (api.signer) { - const payload = new SignerPayload({ - ...eraOptions, - address, - method: _extrinsic.method, - blockNumber: header ? header.number : 0 - }); - - if (api.signer.signPayload) { - const { id, signature } = await api.signer.signPayload(payload.toPayload()); - - // Here we explicitly call `toPayload()` again instead of working with an object - // (reference) as passed to the signer. This means that we are sure that the - // payload data is not modified from our inputs, but the signer - _extrinsic.addSignature(address, signature, payload.toPayload()); - updateId = id; - } else if (api.signer.signRaw) { - const { id, signature } = await api.signer.signRaw(payload.toRaw()); - - // as above, always trust our payload as the signle sourec of truth - _extrinsic.addSignature(address, signature, payload.toPayload()); - updateId = id; - } else if (api.signer.sign) { - console.warn('The Signer.sign interface is deprecated and will be removed in a future version, Swap to using the Signer.signPayload interface instead.'); - - updateId = await api.signer.sign(_extrinsic, address, { + (): Observable => ( + getPrelimState(address, options).pipe( + first(), + mergeMap(async ([nonce, header]): Promise => { + const eraOptions = expandEraOptions(options, { header, nonce }); + + // FIXME This is becoming real messy with all the options - way past + // "a method should fit on a single screen" stage. (Probably want to + // clean this when we remove `api.signer.sign` in the next beta cycle) + if (isKeyringPair(account)) { + this.sign(account, eraOptions); + } else if (api.signer) { + const payload = new SignerPayload({ ...eraOptions, - blockNumber: header ? header.number.toBn() : new BN(0), - genesisHash: api.genesisHash + address, + method: _extrinsic.method, + blockNumber: header ? header.number : 0 }); + + if (api.signer.signPayload) { + const { id, signature } = await api.signer.signPayload(payload.toPayload()); + + // Here we explicitly call `toPayload()` again instead of working with an object + // (reference) as passed to the signer. This means that we are sure that the + // payload data is not modified from our inputs, but the signer + _extrinsic.addSignature(address, signature, payload.toPayload()); + updateId = id; + } else if (api.signer.signRaw) { + const { id, signature } = await api.signer.signRaw(payload.toRaw()); + + // as above, always trust our payload as the signle sourec of truth + _extrinsic.addSignature(address, signature, payload.toPayload()); + updateId = id; + } else if (api.signer.sign) { + console.warn('The Signer.sign interface is deprecated and will be removed in a future version, Swap to using the Signer.signPayload interface instead.'); + + updateId = await api.signer.sign(_extrinsic, address, { + ...eraOptions, + blockNumber: header ? header.number.toBn() : new BN(0), + genesisHash: api.genesisHash + }); + } else { + throw new Error('Invalid signer interface'); + } } else { - throw new Error('Invalid signer interface'); + throw new Error('no signer exists'); } - } else { - throw new Error('no signer exists'); - } - }), - switchMap((): Observable | Observable => { - return isSubscription - ? subscribeObservable(updateId) - : sendObservable(updateId); - }) - ) as Observable) // FIXME This is wrong, SubmittableResult is _not_ a codec + }), + switchMap((): Observable | Observable => { + return isSubscription + ? subscribeObservable(updateId) + : sendObservable(updateId); + }) + ) as Observable) // FIXME This is wrong, SubmittableResult is _not_ a codec )(statusCb); } } From ef0950a99989d67c5b87acadb202c333b3125840 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 21 Aug 2019 16:08:54 +0200 Subject: [PATCH 5/6] Typo in comment --- packages/api/src/base/Decorate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index 38ba559e11d5..27c35bc17810 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -218,7 +218,7 @@ export default abstract class Decorate extends Events { decorated.key = (arg1?: Arg, arg2?: Arg): string => u8aToHex(compactStripLength(creator(creator.meta.type.isDoubleMap ? [arg1, arg2] : arg1))[1]); - // When using double map storage function, user need to path double map key as an array + // When using double map storage function, user need to pass double map key as an array decorated.multi = decorateMethod((args: (Arg | Arg[])[]): Observable => this._rpcCore.state.subscribeStorage( args.map((arg: Arg[] | Arg): [StorageEntry, Arg | Arg[]] => [creator, arg]))); From 7fbbf06624f124d543854281f5c557eb91dc73d6 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 21 Aug 2019 16:42:46 +0200 Subject: [PATCH 6/6] Cleanup descriptions --- packages/api/src/promise/Api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/api/src/promise/Api.ts b/packages/api/src/promise/Api.ts index 52518db63283..2f021cb18163 100644 --- a/packages/api/src/promise/Api.ts +++ b/packages/api/src/promise/Api.ts @@ -17,7 +17,7 @@ interface Tracker { resolve: (value: () => void) => void; } -// extract the arguments and callback parats from a value array possibly containing a callback +// extract the arguments and callback params from a value array possibly containing a callback function extractArgs (args: any[], needsCallback: boolean): [any[], Callback | undefined] { let callback: Callback | undefined; const actualArgs = args.slice(); @@ -34,7 +34,7 @@ function extractArgs (args: any[], needsCallback: boolean): [any[], Callback void) => void, reject: (value: Error) => void): Tracker { let isCompleted = false; const complete = (fn: Function, value: any): void => { @@ -238,7 +238,7 @@ export default class ApiPromise extends ApiBase<'promise'> { } /** - * @description Decores the method for the ApiPromise, where the results are converted to the Promise equivalent + * @description Decorate method for ApiPromise, where the results are converted to the Promise equivalent */ protected decorateMethod (method: Method, options?: DecorateMethodOptions): StorageEntryPromiseOverloads { const needsCallback = options && options.methodName && options.methodName.includes('subscribe');