Codec types (call definitions in API) (#650)
* Codec types (call definitions) * Pass Codec & Subscription generic types * WIP typing * Typing seems ok * Linting * Queryable storage retuns Hash & U64 for .hash and .size * Better typing for SubmittableExtrinsic * Indentation. * Update with needsCallback * Update SubmittableExtrinsic.ts
This commit is contained in:
+94
-84
@@ -4,18 +4,20 @@
|
||||
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
import { Storage } from '@polkadot/storage/types';
|
||||
import { Codec, CodecArg, CodecCallback } from '@polkadot/types/types';
|
||||
import { RxResult } from './rx/types';
|
||||
import {
|
||||
ApiBaseInterface, ApiInterface$Rx, ApiInterface$Events, ApiOptions,
|
||||
DecoratedRpc, DecoratedRpc$Section,
|
||||
DecoratedRpc, DecoratedRpc$Method, DecoratedRpc$Section,
|
||||
Derive, DeriveSection,
|
||||
OnCallFunction,
|
||||
HashResult, U64Result,
|
||||
OnCallDefinition, OnCallFunction,
|
||||
QueryableModuleStorage, QueryableStorage, QueryableStorageFunction,
|
||||
SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics
|
||||
} from './types';
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { map } from 'rxjs/operators';
|
||||
import decorateDerive, { Derive as DeriveInterface } from '@polkadot/api-derive/index';
|
||||
import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
|
||||
import RpcBase from '@polkadot/rpc-core/index';
|
||||
@@ -25,7 +27,6 @@ import registry from '@polkadot/types/codec/typeRegistry';
|
||||
import { Event, Hash, Metadata, Method, RuntimeVersion } from '@polkadot/types/index';
|
||||
import { MethodFunction, ModulesWithMethods } from '@polkadot/types/Method';
|
||||
import { StorageFunction } from '@polkadot/types/StorageKey';
|
||||
import { Codec } from '@polkadot/types/types';
|
||||
import { assert, compactStripLength, isFunction, isObject, isUndefined, logger, u8aToHex } from '@polkadot/util';
|
||||
|
||||
import SubmittableExtrinsic from './SubmittableExtrinsic';
|
||||
@@ -47,20 +48,20 @@ const l = logger('api/decorator');
|
||||
* `api._rx`.
|
||||
*/
|
||||
function rxOnCall (
|
||||
method: OnCallFunction<Observable<Codec | undefined | null>>,
|
||||
params: Array<any>,
|
||||
_isSubscription?: boolean
|
||||
): Observable<Codec | undefined | null> {
|
||||
method: OnCallFunction<RxResult, RxResult>,
|
||||
params: Array<CodecArg> = [],
|
||||
_callback?: CodecCallback
|
||||
): RxResult {
|
||||
return method(...params);
|
||||
}
|
||||
|
||||
export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall> {
|
||||
export default abstract class ApiBase<CodecResult, SubscriptionResult> implements ApiBaseInterface<CodecResult, SubscriptionResult> {
|
||||
private _eventemitter: EventEmitter;
|
||||
private _derive?: Derive<OnCall>;
|
||||
private _extrinsics?: SubmittableExtrinsics<OnCall>;
|
||||
private _derive?: Derive<CodecResult, SubscriptionResult>;
|
||||
private _extrinsics?: SubmittableExtrinsics<CodecResult, SubscriptionResult>;
|
||||
private _genesisHash?: Hash;
|
||||
private _query?: QueryableStorage<OnCall>;
|
||||
private _rpc: DecoratedRpc<OnCall>;
|
||||
private _query?: QueryableStorage<CodecResult, SubscriptionResult>;
|
||||
private _rpc: DecoratedRpc<CodecResult, SubscriptionResult>;
|
||||
protected _rpcBase: RpcBase; // FIXME These two could be merged
|
||||
protected _rpcRx: RpcRx; // FIXME These two could be merged
|
||||
private _runtimeMetadata?: Metadata;
|
||||
@@ -152,10 +153,10 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
get derive (): Derive<OnCall> {
|
||||
get derive (): Derive<CodecResult, SubscriptionResult> {
|
||||
assert(!isUndefined(this._derive), INIT_ERROR);
|
||||
|
||||
return this._derive as Derive<OnCall>;
|
||||
return this._derive as Derive<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,10 +173,10 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
get query (): QueryableStorage<OnCall> {
|
||||
get query (): QueryableStorage<CodecResult, SubscriptionResult> {
|
||||
assert(!isUndefined(this._query), INIT_ERROR);
|
||||
|
||||
return this._query as QueryableStorage<OnCall>;
|
||||
return this._query as QueryableStorage<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +193,7 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
get rpc (): DecoratedRpc<OnCall> {
|
||||
get rpc (): DecoratedRpc<CodecResult, SubscriptionResult> {
|
||||
return this._rpc;
|
||||
}
|
||||
|
||||
@@ -210,10 +211,10 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
get tx (): SubmittableExtrinsics<OnCall> {
|
||||
get tx (): SubmittableExtrinsics<CodecResult, SubscriptionResult> {
|
||||
assert(!isUndefined(this._extrinsics), INIT_ERROR);
|
||||
|
||||
return this._extrinsics as SubmittableExtrinsics<OnCall>;
|
||||
return this._extrinsics as SubmittableExtrinsics<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,6 +267,8 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
return this;
|
||||
}
|
||||
|
||||
protected abstract onCall (method: OnCallFunction<RxResult, RxResult>, params?: Array<CodecArg>, callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult;
|
||||
|
||||
private emit (type: ApiInterface$Events, ...args: Array<any>): void {
|
||||
this._eventemitter.emit(type, ...args);
|
||||
}
|
||||
@@ -323,8 +326,6 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract onCall (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean): OnCall;
|
||||
|
||||
private decorateFunctionMeta (input: MetaDecoration, output: MetaDecoration): MetaDecoration {
|
||||
output.meta = input.meta;
|
||||
output.method = input.method;
|
||||
@@ -338,32 +339,40 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
return output;
|
||||
}
|
||||
|
||||
private decorateRpc<T> (
|
||||
rpc: RpcRx,
|
||||
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
|
||||
): DecoratedRpc<T> {
|
||||
|
||||
private decorateRpc<C, S> (rpc: RpcRx, onCall: OnCallDefinition<C, S>): DecoratedRpc<C, S> {
|
||||
return ['author', 'chain', 'state', 'system'].reduce((result, _sectionName) => {
|
||||
const sectionName = _sectionName as keyof DecoratedRpc<T>;
|
||||
const sectionName = _sectionName as keyof DecoratedRpc<C, S>;
|
||||
|
||||
result[sectionName] = Object.keys(rpc[sectionName]).reduce((section, methodName) => {
|
||||
// FIXME Find a better way to know if a particular method is a subscription or not
|
||||
const isSubscription = methodName.includes('subscribe');
|
||||
const method = (...params: any[]) => onCall(rpc[sectionName][methodName], params, isSubscription);
|
||||
const needsCallback = methodName.includes('subscribe');
|
||||
// These signatures are allowed and exposed here (bit or a stoopid way, but checked
|
||||
// RPCs and we have 3 max args, with subs max one arg... YMMV) -
|
||||
// (arg1?: CodecArg, arg2?: CodecArg, arg3?: CodecArg): CodecResult;
|
||||
// (arg1: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
// (callback: CodecCallback): SubscriptionResult;
|
||||
const method = ((...args: Array<any>): C | S => {
|
||||
let callback: CodecCallback | undefined;
|
||||
let params = args;
|
||||
|
||||
if (args.length && isFunction(args[args.length - 1])) {
|
||||
callback = args[args.length - 1];
|
||||
params = args.slice(0, args.length - 1);
|
||||
}
|
||||
|
||||
return onCall(rpc[sectionName][methodName], params, callback, needsCallback);
|
||||
}) as DecoratedRpc$Method<C, S>;
|
||||
|
||||
section[methodName] = method;
|
||||
|
||||
return section;
|
||||
}, {} as DecoratedRpc$Section<T>);
|
||||
}, {} as DecoratedRpc$Section<C, S>);
|
||||
|
||||
return result;
|
||||
}, {} as DecoratedRpc<T>);
|
||||
}, {} as DecoratedRpc<C, S>);
|
||||
}
|
||||
|
||||
private decorateExtrinsics<T> (
|
||||
extrinsics: ModulesWithMethods,
|
||||
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
|
||||
): SubmittableExtrinsics<T> {
|
||||
|
||||
private decorateExtrinsics<C, S> (extrinsics: ModulesWithMethods, onCall: OnCallDefinition<C, S>): SubmittableExtrinsics<C, S> {
|
||||
return Object.keys(extrinsics).reduce((result, sectionName) => {
|
||||
const section = extrinsics[sectionName];
|
||||
|
||||
@@ -371,27 +380,20 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
result[methodName] = this.decorateExtrinsicEntry(section[methodName], onCall);
|
||||
|
||||
return result;
|
||||
}, {} as SubmittableModuleExtrinsics<T>);
|
||||
}, {} as SubmittableModuleExtrinsics<C, S>);
|
||||
|
||||
return result;
|
||||
}, {} as SubmittableExtrinsics<T>);
|
||||
}, {} as SubmittableExtrinsics<C, S>);
|
||||
}
|
||||
|
||||
private decorateExtrinsicEntry<T> (
|
||||
method: MethodFunction,
|
||||
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
|
||||
): SubmittableExtrinsicFunction<T> {
|
||||
const decorated: any = (...args: Array<any>): SubmittableExtrinsic<T> =>
|
||||
new SubmittableExtrinsic(this._rx as ApiInterface$Rx, onCall, method(...args));
|
||||
private decorateExtrinsicEntry<C, S> (method: MethodFunction, onCall: OnCallDefinition<C, S>): SubmittableExtrinsicFunction<C, S> {
|
||||
const decorated: any = (...params: Array<CodecArg>): SubmittableExtrinsic<C, S> =>
|
||||
new SubmittableExtrinsic(this._rx as ApiInterface$Rx, onCall, method(...params));
|
||||
|
||||
return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction<T>;
|
||||
return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction<C, S>;
|
||||
}
|
||||
|
||||
private decorateStorage<T> (
|
||||
storage: Storage,
|
||||
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
|
||||
): QueryableStorage<T> {
|
||||
|
||||
private decorateStorage<C, S> (storage: Storage, onCall: OnCallDefinition<C, S>): QueryableStorage<C, S> {
|
||||
return Object.keys(storage).reduce((result, sectionName) => {
|
||||
const section = storage[sectionName];
|
||||
|
||||
@@ -399,61 +401,69 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
result[methodName] = this.decorateStorageEntry(section[methodName], onCall);
|
||||
|
||||
return result;
|
||||
}, {} as QueryableModuleStorage<T>);
|
||||
}, {} as QueryableModuleStorage<C, S>);
|
||||
|
||||
return result;
|
||||
}, {} as QueryableStorage<T>);
|
||||
}, {} as QueryableStorage<C, S>);
|
||||
}
|
||||
|
||||
private decorateStorageEntry<T> (
|
||||
method: StorageFunction,
|
||||
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
|
||||
): QueryableStorageFunction<T> {
|
||||
const decorated: any = (...args: any): T =>
|
||||
onCall(
|
||||
(...args: any) => this._rpcRx.state
|
||||
.subscribeStorage([[method, args[0]]])
|
||||
private decorateStorageEntry<C, S> (method: StorageFunction, onCall: OnCallDefinition<C, S>): QueryableStorageFunction<C, S> {
|
||||
// These signatures are allowed and exposed here -
|
||||
// (arg?: CodecArg): CodecResult;
|
||||
// (arg: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
// (callback: CodecCallback): SubscriptionResult;
|
||||
const decorated = ((...args: Array<any>): C | S => {
|
||||
let callback: CodecCallback | undefined;
|
||||
let params = args;
|
||||
|
||||
if (args.length && isFunction(args[args.length - 1])) {
|
||||
callback = args[args.length - 1];
|
||||
params = args.slice(0, args.length - 1);
|
||||
}
|
||||
|
||||
return onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state
|
||||
.subscribeStorage([[method, arg]])
|
||||
.pipe(
|
||||
// errors can occur in the case of malformed methods + args
|
||||
catchError(() => of([])),
|
||||
// state_storage returns an array of values, since we have just subscribed to
|
||||
// a single entry, we pull that from the array and return it as-is
|
||||
map((result: Array<Codec | null | undefined> = []): Codec | null | undefined =>
|
||||
map((result: Array<Codec>): Codec =>
|
||||
result[0]
|
||||
)
|
||||
),
|
||||
args
|
||||
params,
|
||||
callback
|
||||
);
|
||||
}) as QueryableStorageFunction<C, S>;
|
||||
|
||||
decorated.at = (hash: Hash, arg?: any): T =>
|
||||
decorated.at = (hash: Hash | Uint8Array | string, arg?: CodecArg): C =>
|
||||
onCall(
|
||||
// same as above (for single result), in the case of errors on creation, return `undefined`
|
||||
arg => this._rpcRx.state.getStorage([method, arg], hash).pipe(catchError(() => of())),
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorage([method, arg], hash),
|
||||
[arg]
|
||||
);
|
||||
) as C;
|
||||
|
||||
decorated.hash = (arg?: any): T =>
|
||||
// FIXME The unknown cast is needed since the onCall result, `C | S` cannot
|
||||
// be converted from C to the actual result required
|
||||
decorated.hash = (arg?: CodecArg): HashResult<C, S> =>
|
||||
onCall(
|
||||
arg => this._rpcRx.state.getStorageSize([method, arg]).pipe(catchError(() => of())),
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]),
|
||||
[arg]
|
||||
);
|
||||
) as unknown as HashResult<C, S>;
|
||||
|
||||
decorated.size = (arg?: any): T =>
|
||||
// FIXME as above...
|
||||
decorated.size = (arg?: CodecArg): U64Result<C, S> =>
|
||||
onCall(
|
||||
arg => this._rpcRx.state.getStorageSize([method, arg]).pipe(catchError(() => of())),
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]),
|
||||
[arg]
|
||||
);
|
||||
) as unknown as U64Result<C, S>;
|
||||
|
||||
decorated.key = (arg?: any): string =>
|
||||
decorated.key = (arg?: CodecArg): string =>
|
||||
u8aToHex(compactStripLength(method(arg))[1]);
|
||||
|
||||
return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction<T>;
|
||||
return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction<C, S>;
|
||||
}
|
||||
|
||||
private decorateDerive<T> (
|
||||
apiRx: ApiInterface$Rx,
|
||||
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
|
||||
): Derive<T> {
|
||||
private decorateDerive<C, S> (apiRx: ApiInterface$Rx, onCall: OnCallDefinition<C, S>): Derive<C, S> {
|
||||
const derive = decorateDerive(apiRx);
|
||||
|
||||
return Object.keys(derive).reduce((result, _sectionName) => {
|
||||
@@ -464,9 +474,9 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
|
||||
section[methodName] = method;
|
||||
|
||||
return section;
|
||||
}, {} as DeriveSection<T>);
|
||||
}, {} as DeriveSection<C, S>);
|
||||
|
||||
return result;
|
||||
}, {} as Derive<T>);
|
||||
}, {} as Derive<C, S>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,84 +3,138 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringPair } from '@polkadot/keyring/types';
|
||||
import { Extrinsic, ExtrinsicStatus, Index, Method, SignedBlock } from '@polkadot/types/index';
|
||||
import { AnyNumber, AnyU8a } from '@polkadot/types/types';
|
||||
import { ApiInterface$Rx, OnCallFunction, SubmittableSendResult } from './types';
|
||||
import { Extrinsic, ExtrinsicStatus, EventRecord, Index, Method, SignedBlock, Text } from '@polkadot/types/index';
|
||||
import { AnyNumber, AnyU8a, CodecCallback } from '@polkadot/types/types';
|
||||
import { ApiInterface$Rx, OnCallDefinition } from './types';
|
||||
|
||||
import { Observable, of, combineLatest } from 'rxjs';
|
||||
import { first, map, switchMap } from 'rxjs/operators';
|
||||
import { Struct, Vector } from '@polkadot/types/codec';
|
||||
|
||||
import filterEvents from './util/filterEvents';
|
||||
|
||||
export default class SubmittableExtrinsic<OnCall> extends Extrinsic {
|
||||
private _api: ApiInterface$Rx;
|
||||
private _onCall: OnCallFunction<OnCall>;
|
||||
// Re-introduce once we use submitExtrinsic as well
|
||||
// type SumbitableResultResult<CodecResult, SubscriptionResult> =
|
||||
// CodecResult extends Observable<any>
|
||||
// ? Observable<SubmittableResult>
|
||||
// : Promise<SubmittableResult>;
|
||||
|
||||
constructor (api: ApiInterface$Rx, onCall: OnCallFunction<OnCall>, extrinsic: Extrinsic | Method) {
|
||||
type SumbitableResultSubscription<CodecResult, SubscriptionResult> =
|
||||
SubscriptionResult extends Observable<any>
|
||||
? Observable<SubmittableResult>
|
||||
: Promise<() => void>;
|
||||
|
||||
export class SubmittableResult extends Struct {
|
||||
constructor (value?: any) {
|
||||
super({
|
||||
events: Vector.with(EventRecord),
|
||||
status: ExtrinsicStatus,
|
||||
type: Text
|
||||
}, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description the contained events
|
||||
*/
|
||||
get events (): Array<EventRecord> {
|
||||
return this.get('events') as Vector<EventRecord>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description the status
|
||||
*/
|
||||
get status (): ExtrinsicStatus {
|
||||
return this.get('status') as ExtrinsicStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description the type
|
||||
*/
|
||||
get type (): string {
|
||||
return (this.get('type') as Text).toString();
|
||||
}
|
||||
}
|
||||
|
||||
export default class SubmittableExtrinsic<CodecResult, SubscriptionResult> extends Extrinsic {
|
||||
private _api: ApiInterface$Rx;
|
||||
private _onCall: OnCallDefinition<CodecResult, SubscriptionResult>;
|
||||
|
||||
constructor (api: ApiInterface$Rx, onCall: OnCallDefinition<CodecResult, SubscriptionResult>, extrinsic: Extrinsic | Method) {
|
||||
super(extrinsic);
|
||||
|
||||
this._api = api;
|
||||
this._onCall = onCall;
|
||||
}
|
||||
|
||||
private trackStatus (status: ExtrinsicStatus, statusCb?: (result: SubmittableSendResult) => any): Observable<SubmittableSendResult> {
|
||||
private statusObservable (status: ExtrinsicStatus): Observable<SubmittableResult> {
|
||||
if (status.type !== 'Finalised') {
|
||||
return this._onCall(
|
||||
() => of({
|
||||
return of(
|
||||
new SubmittableResult({
|
||||
status,
|
||||
type: status.type
|
||||
}),
|
||||
[statusCb],
|
||||
false
|
||||
) as unknown as Observable<SubmittableSendResult>;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const blockHash = status.asFinalised;
|
||||
|
||||
return this._onCall(
|
||||
() => combineLatest(
|
||||
this._api.rpc.chain.getBlock(blockHash),
|
||||
this._api.query.system.events.at(blockHash)
|
||||
).pipe(
|
||||
map(([signedBlock, allEvents]) => ({
|
||||
events: filterEvents(this.hash, signedBlock as SignedBlock, allEvents as any),
|
||||
return combineLatest(
|
||||
this._api.rpc.chain.getBlock(blockHash) as Observable<SignedBlock>,
|
||||
this._api.query.system.events.at(blockHash) as Observable<Vector<EventRecord>>
|
||||
).pipe(
|
||||
map(([signedBlock, allEvents]) =>
|
||||
new SubmittableResult({
|
||||
events: filterEvents(this.hash, signedBlock, allEvents),
|
||||
status,
|
||||
type: status.type
|
||||
}))
|
||||
),
|
||||
[statusCb],
|
||||
false
|
||||
) as unknown as Observable<SubmittableSendResult>;
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
send (statusCb?: (result: SubmittableSendResult) => any): Observable<SubmittableSendResult> {
|
||||
private sendObservable (): Observable<SubmittableResult> {
|
||||
return (this._api.rpc.author
|
||||
.submitAndWatchExtrinsic(this) as Observable<ExtrinsicStatus>)
|
||||
.pipe(
|
||||
switchMap((status) =>
|
||||
this.statusObservable(status)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME We currently only cater for callback in Promise-world, submitExtrinsic -> Hash
|
||||
// should also be catered for returning SumbitableResultResult
|
||||
send (statusCb?: (result: SubmittableResult) => any): SumbitableResultSubscription<CodecResult, SubscriptionResult> {
|
||||
return this._onCall(
|
||||
() => (this._api.rpc.author
|
||||
.submitAndWatchExtrinsic(this) as Observable<ExtrinsicStatus>)
|
||||
.pipe(switchMap((status) => this.trackStatus(status, statusCb))),
|
||||
[statusCb],
|
||||
() => this.sendObservable(),
|
||||
[],
|
||||
statusCb as CodecCallback,
|
||||
true
|
||||
) as unknown as Observable<SubmittableSendResult>;
|
||||
) as unknown as SumbitableResultSubscription<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic<OnCall> {
|
||||
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic<CodecResult, SubscriptionResult> {
|
||||
super.sign(signerPair, nonce, blockHash || this._api.genesisHash);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
signAndSend (signerPair: KeyringPair, statusCb?: (result: SubmittableSendResult) => any): Observable<SubmittableSendResult> {
|
||||
// FIXME As per previous FIXME on send, same applies
|
||||
signAndSend (signerPair: KeyringPair, statusCb?: (result: SubmittableResult) => any): SumbitableResultSubscription<CodecResult, SubscriptionResult> {
|
||||
return this._onCall(
|
||||
() => this._api.query.system
|
||||
.accountNonce(signerPair.address())
|
||||
() => (this._api.query.system
|
||||
.accountNonce(signerPair.address()) as Observable<Index>)
|
||||
.pipe(
|
||||
first(),
|
||||
switchMap((nonce) =>
|
||||
this.sign(signerPair, nonce as Index).send(statusCb)
|
||||
this
|
||||
.sign(signerPair, nonce)
|
||||
.sendObservable()
|
||||
)
|
||||
),
|
||||
[statusCb],
|
||||
false
|
||||
) as unknown as Observable<SubmittableSendResult>;
|
||||
[],
|
||||
statusCb as CodecCallback,
|
||||
true
|
||||
) as unknown as SumbitableResultSubscription<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { PromiseSubscription } from './types';
|
||||
import { SubscriptionResult } from './types';
|
||||
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
export type CombinatorCallback = (value: Array<any>) => any;
|
||||
export type CombinatorFunction = {
|
||||
(cb: (value: any) => any): PromiseSubscription
|
||||
(cb: (value: any) => any): SubscriptionResult
|
||||
};
|
||||
|
||||
export default class Combinator {
|
||||
@@ -18,7 +18,7 @@ export default class Combinator {
|
||||
protected _fns: Array<CombinatorFunction> = [];
|
||||
protected _isActive: boolean = true;
|
||||
protected _results: Array<any> = [];
|
||||
protected _subscriptions: Array<PromiseSubscription> = [];
|
||||
protected _subscriptions: Array<SubscriptionResult> = [];
|
||||
|
||||
constructor (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback) {
|
||||
this._callback = callback;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { PromiseSubscription } from './types';
|
||||
import { SubscriptionResult } from './types';
|
||||
|
||||
import Combinator from './Combinator';
|
||||
|
||||
describe('Combinator', () => {
|
||||
let fns: Array<(value: any) => void> = [];
|
||||
const storeFn = async (cb: (value: any) => void): PromiseSubscription => {
|
||||
const storeFn = async (cb: (value: any) => void): SubscriptionResult => {
|
||||
fns.push(cb);
|
||||
|
||||
return () => undefined;
|
||||
|
||||
@@ -3,19 +3,18 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
import { ApiOptions } from '../types';
|
||||
import { ApiPromiseInterface, OnCall, PromiseSubscription } from './types';
|
||||
import { CodecArg, CodecCallback } from '@polkadot/types/types';
|
||||
import { RxResult } from '../rx/types';
|
||||
import { ApiOptions, OnCallFunction } from '../types';
|
||||
import { ApiPromiseInterface, CodecResult, SubscriptionResult } from './types';
|
||||
|
||||
import { EMPTY, Observable } from 'rxjs';
|
||||
import { EMPTY } from 'rxjs';
|
||||
import { catchError, first, tap } from 'rxjs/operators';
|
||||
import { Codec } from '@polkadot/types/types';
|
||||
import { isFunction } from '@polkadot/util';
|
||||
import { isFunction, assert } from '@polkadot/util';
|
||||
|
||||
import ApiBase from '../Base';
|
||||
import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator';
|
||||
|
||||
type RxFn = (...params: Array<any>) => Observable<Codec | undefined | null>;
|
||||
|
||||
/**
|
||||
* # @polkadot/api/promise
|
||||
*
|
||||
@@ -102,7 +101,7 @@ type RxFn = (...params: Array<any>) => Observable<Codec | undefined | null>;
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInterface {
|
||||
export default class ApiPromise extends ApiBase<CodecResult, SubscriptionResult> implements ApiPromiseInterface {
|
||||
private _isReady: Promise<ApiPromise>;
|
||||
|
||||
/**
|
||||
@@ -185,7 +184,7 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
async combineLatest (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback): PromiseSubscription {
|
||||
async combineLatest (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback): SubscriptionResult {
|
||||
const combinator = new Combinator(fns, callback);
|
||||
|
||||
return (): void => {
|
||||
@@ -193,15 +192,11 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
|
||||
};
|
||||
}
|
||||
|
||||
protected onCall (method: RxFn, params: Array<any>, isSubscription?: boolean): OnCall {
|
||||
if (!params || params.length === 0 || isSubscription === false) {
|
||||
return method(...params).pipe(first()).toPromise();
|
||||
}
|
||||
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = [], callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult {
|
||||
// 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 cb = params[params.length - 1];
|
||||
const remainingParams = params.slice(0, - 1);
|
||||
|
||||
if (!isFunction(cb)) {
|
||||
if (!callback) {
|
||||
return method(...params).pipe(first()).toPromise();
|
||||
}
|
||||
|
||||
@@ -210,7 +205,7 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
|
||||
// tslint:disable-next-line
|
||||
return new Promise((resolve, reject) => {
|
||||
let isCompleted = false;
|
||||
const subscription = method(...remainingParams)
|
||||
const subscription = method(...params)
|
||||
.pipe(
|
||||
// if we find an error (invalid params, etc), reject the promise
|
||||
catchError((error) => {
|
||||
@@ -232,7 +227,7 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
|
||||
}
|
||||
})
|
||||
)
|
||||
.subscribe(cb);
|
||||
}) as PromiseSubscription;
|
||||
.subscribe(callback);
|
||||
}) as SubscriptionResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ import { QueryableStorageFunction as QueryableStorageFunctionBase, SubmittableEx
|
||||
import ApiBase from '../Base';
|
||||
import SubmittableExtrinsicBase from '../SubmittableExtrinsic';
|
||||
|
||||
export type PromiseSubscription = Promise<() => any>;
|
||||
export type SubscriptionResult = Promise<() => any>;
|
||||
|
||||
export type OnCall = Promise<Codec | null | undefined> | PromiseSubscription;
|
||||
export type CodecResult = Promise<Codec>;
|
||||
|
||||
export interface ApiPromiseInterface extends ApiBase<OnCall> {
|
||||
export interface ApiPromiseInterface extends ApiBase<CodecResult, SubscriptionResult> {
|
||||
readonly isReady: Promise<ApiPromiseInterface>;
|
||||
}
|
||||
|
||||
export type QueryableStorageFunction = QueryableStorageFunctionBase<OnCall>;
|
||||
export type SubmittableExtrinsic = SubmittableExtrinsicBase<OnCall>;
|
||||
export type SubmittableExtrinsicFunction = SubmittableExtrinsicFunctionBase<OnCall>;
|
||||
export type QueryableStorageFunction = QueryableStorageFunctionBase<CodecResult, SubscriptionResult>;
|
||||
export type SubmittableExtrinsic = SubmittableExtrinsicBase<CodecResult, SubscriptionResult>;
|
||||
export type SubmittableExtrinsicFunction = SubmittableExtrinsicFunctionBase<CodecResult, SubscriptionResult>;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
import { ApiRxInterface, OnCall } from './types';
|
||||
import { CodecArg } from '@polkadot/types/types';
|
||||
import { ApiRxInterface, RxResult } from './types';
|
||||
import { ApiOptions, OnCallFunction } from '../types';
|
||||
|
||||
import { Observable, from } from 'rxjs';
|
||||
import { Codec } from '@polkadot/types/types';
|
||||
|
||||
import ApiBase from '../Base';
|
||||
|
||||
@@ -112,7 +112,7 @@ import ApiBase from '../Base';
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export default class ApiRx extends ApiBase<OnCall> implements ApiRxInterface {
|
||||
export default class ApiRx extends ApiBase<RxResult, RxResult> implements ApiRxInterface {
|
||||
private _isReady: Observable<ApiRx>;
|
||||
|
||||
/**
|
||||
@@ -190,7 +190,7 @@ export default class ApiRx extends ApiBase<OnCall> implements ApiRxInterface {
|
||||
return this._isReady;
|
||||
}
|
||||
|
||||
protected onCall (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>): OnCall {
|
||||
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = []): RxResult {
|
||||
return method(...params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import { Codec } from '@polkadot/types/types';
|
||||
|
||||
import ApiBase from '../Base';
|
||||
|
||||
export type OnCall = Observable<Codec | null | undefined>;
|
||||
export type RxResult = Observable<Codec>;
|
||||
|
||||
export interface ApiRxInterface extends ApiBase<OnCall> {
|
||||
export interface ApiRxInterface extends ApiBase<RxResult, RxResult> {
|
||||
readonly isConnected: Observable<boolean>;
|
||||
readonly isReady: Observable<ApiRxInterface>;
|
||||
}
|
||||
|
||||
+66
-53
@@ -5,79 +5,92 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
import { RpcRxInterface$Events } from '@polkadot/rpc-rx/types';
|
||||
import { EventRecord, ExtrinsicStatus, Hash, Metadata, RuntimeVersion } from '@polkadot/types/index';
|
||||
import { Codec, Constructor } from '@polkadot/types/types';
|
||||
import { Hash, Metadata, RuntimeVersion, u64 as U64 } from '@polkadot/types/index';
|
||||
import { CodecArg, CodecCallback, Constructor } from '@polkadot/types/types';
|
||||
import { MethodFunction } from '@polkadot/types/Method';
|
||||
import { StorageFunction } from '@polkadot/types/StorageKey';
|
||||
|
||||
import { RxResult } from './rx/types';
|
||||
import SubmittableExtrinsic from './SubmittableExtrinsic';
|
||||
|
||||
export type OnCallFunction<OnCall> = (...args: any[]) => OnCall;
|
||||
export type OnCallDefinition<CodecResult, SubscriptionResult> = (method: OnCallFunction<RxResult, RxResult>, params?: Array<CodecArg>, callback?: CodecCallback, needsCallback?: boolean) => CodecResult | SubscriptionResult;
|
||||
|
||||
export type DecoratedRpc$Method<OnCall> = (...params: Array<any>) => OnCall;
|
||||
export type OnCallFunction<CodecResult, SubscriptionResult> = (...params: Array<CodecArg>) => CodecResult | SubscriptionResult;
|
||||
|
||||
export interface DecoratedRpc$Section<OnCall> {
|
||||
[index: string]: DecoratedRpc$Method<OnCall>;
|
||||
// checked against max. params in jsonrpc, 1 for subs, 3 without
|
||||
export interface DecoratedRpc$Method<CodecResult, SubscriptionResult> {
|
||||
(callback: CodecCallback): SubscriptionResult;
|
||||
(arg1: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
(arg1?: CodecArg, arg2?: CodecArg, arg3?: CodecArg): CodecResult;
|
||||
}
|
||||
|
||||
export interface DecoratedRpc<OnCall> {
|
||||
author: DecoratedRpc$Section<OnCall>;
|
||||
chain: DecoratedRpc$Section<OnCall>;
|
||||
state: DecoratedRpc$Section<OnCall>;
|
||||
system: DecoratedRpc$Section<OnCall>;
|
||||
export interface DecoratedRpc$Section<CodecResult, SubscriptionResult> {
|
||||
[index: string]: DecoratedRpc$Method<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
interface QueryableStorageFunctionBase<OnCall> extends StorageFunction {
|
||||
(arg?: any): OnCall;
|
||||
at: (hash: Uint8Array | string, arg?: any) => OnCall;
|
||||
hash: (arg?: any) => OnCall; // Observable<Hash>
|
||||
key: (arg?: any) => string;
|
||||
size: (arg?: any) => OnCall; // Observable<U64>
|
||||
export interface DecoratedRpc<CodecResult, SubscriptionResult> {
|
||||
author: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
|
||||
chain: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
|
||||
state: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
|
||||
system: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
interface QueryableStorageFunctionPromise<OnCall> extends QueryableStorageFunctionBase<OnCall> {
|
||||
(arg?: any, cb?: (value?: Codec) => any): OnCall;
|
||||
export type HashResult<CodecResult, SubscriptionResult> =
|
||||
CodecResult extends Observable<any>
|
||||
? Observable<Hash>
|
||||
: Promise<Hash>;
|
||||
|
||||
export type U64Result<CodecResult, SubscriptionResult> =
|
||||
CodecResult extends Observable<any>
|
||||
? Observable<U64>
|
||||
: Promise<U64>;
|
||||
|
||||
export interface QueryableStorageFunctionBase<CodecResult, SubscriptionResult> extends StorageFunction {
|
||||
(arg?: CodecArg): CodecResult;
|
||||
at: (hash: Hash | Uint8Array | string, arg?: CodecArg) => CodecResult;
|
||||
hash: (arg?: CodecArg) => HashResult<CodecResult, SubscriptionResult>;
|
||||
key: (arg?: CodecArg) => string;
|
||||
size: (arg?: CodecArg) => U64Result<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export type QueryableStorageFunction<OnCall> =
|
||||
OnCall extends Observable<any>
|
||||
? QueryableStorageFunctionBase<OnCall>
|
||||
: QueryableStorageFunctionPromise<OnCall>;
|
||||
|
||||
export interface QueryableModuleStorage<OnCall> {
|
||||
[index: string]: QueryableStorageFunction<OnCall>;
|
||||
interface QueryableStorageFunctionPromise<CodecResult, SubscriptionResult> extends QueryableStorageFunctionBase<CodecResult, SubscriptionResult> {
|
||||
(callback: CodecCallback): SubscriptionResult;
|
||||
(arg: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
}
|
||||
|
||||
export interface QueryableStorage<OnCall> {
|
||||
[index: string]: QueryableModuleStorage<OnCall>;
|
||||
export type QueryableStorageFunction<CodecResult, SubscriptionResult> =
|
||||
CodecResult extends Observable<any>
|
||||
? QueryableStorageFunctionBase<CodecResult, SubscriptionResult>
|
||||
: QueryableStorageFunctionPromise<CodecResult, SubscriptionResult>;
|
||||
|
||||
export interface QueryableModuleStorage<CodecResult, SubscriptionResult> {
|
||||
[index: string]: QueryableStorageFunction<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export interface SubmittableExtrinsicFunction<OnCall> extends MethodFunction {
|
||||
(...args: any[]): SubmittableExtrinsic<OnCall>;
|
||||
export interface QueryableStorage<CodecResult, SubscriptionResult> {
|
||||
[index: string]: QueryableModuleStorage<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export interface SubmittableModuleExtrinsics<OnCall> {
|
||||
[index: string]: SubmittableExtrinsicFunction<OnCall>;
|
||||
export interface SubmittableExtrinsicFunction<CodecResult, SubscriptionResult> extends MethodFunction {
|
||||
(...params: Array<CodecArg>): SubmittableExtrinsic<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export interface SubmittableExtrinsics<OnCall> {
|
||||
[index: string]: SubmittableModuleExtrinsics<OnCall>;
|
||||
export interface SubmittableModuleExtrinsics<CodecResult, SubscriptionResult> {
|
||||
[index: string]: SubmittableExtrinsicFunction<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export type SubmittableSendResult = {
|
||||
events?: Array<EventRecord>,
|
||||
status: ExtrinsicStatus,
|
||||
type: string
|
||||
};
|
||||
|
||||
export type DeriveMethod<OnCall> = (...params: Array<any>) => OnCall;
|
||||
|
||||
export interface DeriveSection<OnCall> {
|
||||
[index: string]: DeriveMethod<OnCall>;
|
||||
export interface SubmittableExtrinsics<CodecResult, SubscriptionResult> {
|
||||
[index: string]: SubmittableModuleExtrinsics<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export interface Derive<OnCall> {
|
||||
[index: string]: DeriveSection<OnCall>;
|
||||
export type DeriveMethod<CodecResult, SubscriptionResult> = (...params: Array<CodecArg>) => CodecResult | SubscriptionResult;
|
||||
|
||||
export interface DeriveSection<CodecResult, SubscriptionResult> {
|
||||
[index: string]: DeriveMethod<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export interface Derive<CodecResult, SubscriptionResult> {
|
||||
[index: string]: DeriveSection<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export interface ApiOptions {
|
||||
@@ -93,22 +106,22 @@ export interface ApiOptions {
|
||||
types?: { [name: string]: Constructor };
|
||||
}
|
||||
|
||||
export interface ApiInterface$Decorated<OnCall> {
|
||||
export interface ApiInterface$Decorated<CodecResult, SubscriptionResult> {
|
||||
genesisHash: Hash;
|
||||
hasSubscriptions: boolean;
|
||||
runtimeMetadata: Metadata;
|
||||
runtimeVersion: RuntimeVersion;
|
||||
derive: Derive<OnCall>;
|
||||
query: QueryableStorage<OnCall>;
|
||||
rpc: DecoratedRpc<OnCall>;
|
||||
tx: SubmittableExtrinsics<OnCall>;
|
||||
derive: Derive<CodecResult, SubscriptionResult>;
|
||||
query: QueryableStorage<CodecResult, SubscriptionResult>;
|
||||
rpc: DecoratedRpc<CodecResult, SubscriptionResult>;
|
||||
tx: SubmittableExtrinsics<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export type ApiInterface$Rx = ApiInterface$Decorated<Observable<Codec | null | undefined>>;
|
||||
export type ApiInterface$Rx = ApiInterface$Decorated<RxResult, RxResult>;
|
||||
|
||||
export type ApiInterface$Events = RpcRxInterface$Events | 'ready';
|
||||
|
||||
export interface ApiBaseInterface<OnCall> extends Readonly<ApiInterface$Decorated<OnCall>> {
|
||||
export interface ApiBaseInterface<CodecResult, SubscriptionResult> extends Readonly<ApiInterface$Decorated<CodecResult, SubscriptionResult>> {
|
||||
on: (type: ApiInterface$Events, handler: (...args: Array<any>) => any) => this;
|
||||
once: (type: ApiInterface$Events, handler: (...args: Array<any>) => any) => this;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
export type CodecArg = Codec | BN | Boolean | String | Uint8Array | boolean | number | string | undefined | CodecArgArray | CodecArgObject;
|
||||
|
||||
export type CodecCallback <T extends Codec = Codec> = (result: T) => any;
|
||||
|
||||
interface CodecArgObject {
|
||||
[index: string]: CodecArg;
|
||||
}
|
||||
|
||||
interface CodecArgArray extends Array<CodecArg> { }
|
||||
|
||||
export type AnyNumber = BN | Uint8Array | number | string;
|
||||
|
||||
export type AnyString = string | String;
|
||||
|
||||
Reference in New Issue
Block a user