Bump common (#2832)
* Bump common * Update for new common non-default exports * Don't export createSubmittable * Adjust exports * default export adjustments * Some additional non-default exports
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@polkadot/api": "2.6.2-0",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"bn.js": "^5.1.3",
|
||||
"rxjs": "^6.6.3"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import Abi from './Abi';
|
||||
import { Abi } from '.';
|
||||
|
||||
import * as testContracts from '../test/contracts';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AbiConstructor, AbiEvent, AbiMessage, AbiParam, DecodedEvent, DecodedMe
|
||||
|
||||
import { assert, assertReturn, compactAddLength, compactStripLength, isNumber, isObject, isString, logger, stringCamelCase, u8aConcat, u8aToHex } from '@polkadot/util';
|
||||
|
||||
import MetaRegistry from './MetaRegistry';
|
||||
import { MetaRegistry } from './MetaRegistry';
|
||||
|
||||
const l = logger('Abi');
|
||||
|
||||
@@ -21,7 +21,7 @@ function findMessage <T extends AbiMessage> (list: T[], messageOrId: T | string
|
||||
return assertReturn(message, `Attempted to call an invalid contract interface, ${JSON.stringify(messageOrId)}`);
|
||||
}
|
||||
|
||||
export default class Abi {
|
||||
export class Abi {
|
||||
readonly #events: AbiEvent[];
|
||||
|
||||
public readonly constructors: AbiConstructor[];
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import Abi from './Abi';
|
||||
import { Abi } from '.';
|
||||
|
||||
import * as testContracts from '../test/contracts';
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const PRIMITIVE_ALIAS: Record<string, keyof InterfaceTypes> = {
|
||||
|
||||
const PRIMITIVE_ALWAYS = ['AccountId', 'AccountIndex', 'Address', 'Balance'];
|
||||
|
||||
export default class MetaRegistry extends TypeRegistry {
|
||||
export class MetaRegistry extends TypeRegistry {
|
||||
public readonly metaTypeDefs: TypeDef[] = [];
|
||||
|
||||
#siTypes: SiType[] = [];
|
||||
|
||||
@@ -7,9 +7,9 @@ import { AnyJson, Registry } from '@polkadot/types/types';
|
||||
import ApiBase from '@polkadot/api/base';
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import { Abi } from '../Abi';
|
||||
|
||||
export default abstract class Base<ApiType extends ApiTypes> {
|
||||
export abstract class Base<ApiType extends ApiTypes> {
|
||||
public readonly abi: Abi;
|
||||
|
||||
public readonly api: ApiBase<ApiType>;
|
||||
|
||||
@@ -13,10 +13,10 @@ import { SubmittableResult } from '@polkadot/api';
|
||||
import ApiBase from '@polkadot/api/base';
|
||||
import { isUndefined, stringCamelCase } from '@polkadot/util';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import { Abi } from '../Abi';
|
||||
import { applyOnEvent } from '../util';
|
||||
import Base from './Base';
|
||||
import Contract from './Contract';
|
||||
import { Base } from './Base';
|
||||
import { Contract } from './Contract';
|
||||
|
||||
export class BlueprintSubmittableResult<ApiType extends ApiTypes> extends SubmittableResult {
|
||||
public readonly contract?: Contract<ApiType>;
|
||||
@@ -28,7 +28,7 @@ export class BlueprintSubmittableResult<ApiType extends ApiTypes> extends Submit
|
||||
}
|
||||
}
|
||||
|
||||
export default class Blueprint<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
export class Blueprint<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
public readonly codeHash: Hash;
|
||||
|
||||
readonly #tx: MapConstructorExec<ApiType> = {};
|
||||
|
||||
@@ -10,10 +10,10 @@ import { SubmittableResult } from '@polkadot/api';
|
||||
import ApiBase from '@polkadot/api/base';
|
||||
import { compactAddLength, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import { Abi } from '../Abi';
|
||||
import { applyOnEvent } from '../util';
|
||||
import Base from './Base';
|
||||
import Blueprint from './Blueprint';
|
||||
import { Base } from './Base';
|
||||
import { Blueprint } from './Blueprint';
|
||||
|
||||
export class CodeSubmittableResult<ApiType extends ApiTypes> extends SubmittableResult {
|
||||
public readonly blueprint?: Blueprint<ApiType>;
|
||||
@@ -25,7 +25,7 @@ export class CodeSubmittableResult<ApiType extends ApiTypes> extends Submittable
|
||||
}
|
||||
}
|
||||
|
||||
export default class Code<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
export class Code<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
public readonly code: Uint8Array;
|
||||
|
||||
constructor (api: ApiBase<ApiType>, abi: AnyJson | Abi, wasm: Uint8Array | string | Buffer, decorateMethod: DecorateMethod<ApiType>) {
|
||||
|
||||
@@ -14,9 +14,9 @@ import { SubmittableResult } from '@polkadot/api';
|
||||
import ApiBase from '@polkadot/api/base';
|
||||
import { assert, bnToBn, isFunction, isUndefined, logger, stringCamelCase } from '@polkadot/util';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import { Abi } from '../Abi';
|
||||
import { applyOnEvent, formatData } from '../util';
|
||||
import Base from './Base';
|
||||
import { Base } from './Base';
|
||||
|
||||
// As per Rust, 5 * GAS_PER_SEC
|
||||
const MAX_CALL_GAS = new BN(5_000_000_000_000).subn(1);
|
||||
@@ -60,7 +60,7 @@ function mapExecResult (registry: Registry, json: Record<string, AnyJson>): Cont
|
||||
return registry.createType('ContractExecResult', { result: { err: { other: 'unknown' } } });
|
||||
}
|
||||
|
||||
export default class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
export class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||
public readonly address: AccountId;
|
||||
|
||||
readonly #tx: MapMessageExec<ApiType> = {};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { default as Blueprint, BlueprintSubmittableResult } from './Blueprint';
|
||||
export { default as Code, CodeSubmittableResult } from './Code';
|
||||
export { default as Contract } from './Contract';
|
||||
export { Blueprint, BlueprintSubmittableResult } from './Blueprint';
|
||||
export { Code, CodeSubmittableResult } from './Code';
|
||||
export { Contract } from './Contract';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { default as Abi } from './Abi';
|
||||
export { Abi } from './Abi';
|
||||
|
||||
export { Blueprint as BlueprintRx, Code as CodeRx, Contract as ContractRx } from './rx';
|
||||
export { Blueprint as BlueprintPromise, Code as CodePromise, Contract as ContractPromise } from './promise';
|
||||
|
||||
@@ -7,10 +7,10 @@ import { AnyJson } from '@polkadot/types/types';
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { decorateMethod } from '@polkadot/api/promise';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import BaseBlueprint from '../base/Blueprint';
|
||||
import { Abi } from '../Abi';
|
||||
import { Blueprint as BaseBlueprint } from '../base';
|
||||
|
||||
export default class Blueprint extends BaseBlueprint<'promise'> {
|
||||
export class Blueprint extends BaseBlueprint<'promise'> {
|
||||
constructor (api: ApiPromise, abi: AnyJson | Abi, codeHash: string | Hash) {
|
||||
super(api, abi, codeHash, decorateMethod);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ import { ApiPromise } from '@polkadot/api';
|
||||
import { decorateMethod } from '@polkadot/api/promise';
|
||||
import { AnyJson } from '@polkadot/types/types';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import BaseCode from '../base/Code';
|
||||
import { Abi } from '../Abi';
|
||||
import { Code as BaseCode } from '../base';
|
||||
|
||||
export default class Code extends BaseCode<'promise'> {
|
||||
export class Code extends BaseCode<'promise'> {
|
||||
constructor (api: ApiPromise, abi: AnyJson | Abi, wasm: Uint8Array | string | Buffer) {
|
||||
super(api, abi, wasm, decorateMethod);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import { AnyJson } from '@polkadot/types/types';
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { decorateMethod } from '@polkadot/api/promise';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import BaseContract from '../base/Contract';
|
||||
import { Abi } from '../Abi';
|
||||
import { Contract as BaseContract } from '../base';
|
||||
|
||||
export default class Contract extends BaseContract<'promise'> {
|
||||
export class Contract extends BaseContract<'promise'> {
|
||||
constructor (api: ApiPromise, abi: AnyJson | Abi, address: string | AccountId) {
|
||||
super(api, abi, address, decorateMethod);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { default as Blueprint } from './Blueprint';
|
||||
export { default as Code } from './Code';
|
||||
export { default as Contract } from './Contract';
|
||||
export { Blueprint } from './Blueprint';
|
||||
export { Code } from './Code';
|
||||
export { Contract } from './Contract';
|
||||
|
||||
@@ -7,10 +7,10 @@ import { AnyJson } from '@polkadot/types/types';
|
||||
import { ApiRx } from '@polkadot/api';
|
||||
import { decorateMethod } from '@polkadot/api/rx';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import BaseBlueprint from '../base/Blueprint';
|
||||
import { Abi } from '../Abi';
|
||||
import { Blueprint as BaseBlueprint } from '../base';
|
||||
|
||||
export default class Blueprint extends BaseBlueprint<'rxjs'> {
|
||||
export class Blueprint extends BaseBlueprint<'rxjs'> {
|
||||
constructor (api: ApiRx, abi: AnyJson | Abi, codeHash: string | Hash) {
|
||||
super(api, abi, codeHash, decorateMethod);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import { AnyJson } from '@polkadot/types/types';
|
||||
import { ApiRx } from '@polkadot/api';
|
||||
import { decorateMethod } from '@polkadot/api/rx';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import BaseCode from '../base/Code';
|
||||
import { Abi } from '../Abi';
|
||||
import { Code as BaseCode } from '../base';
|
||||
|
||||
export default class Code extends BaseCode<'rxjs'> {
|
||||
export class Code extends BaseCode<'rxjs'> {
|
||||
constructor (api: ApiRx, abi: AnyJson | Abi, wasm: Uint8Array | string | Buffer) {
|
||||
super(api, abi, wasm, decorateMethod);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import { AnyJson } from '@polkadot/types/types';
|
||||
import { ApiRx } from '@polkadot/api';
|
||||
import { decorateMethod } from '@polkadot/api/rx';
|
||||
|
||||
import Abi from '../Abi';
|
||||
import BaseContract from '../base/Contract';
|
||||
import { Abi } from '../Abi';
|
||||
import { Contract as BaseContract } from '../base';
|
||||
|
||||
export default class Contract extends BaseContract<'rxjs'> {
|
||||
export class Contract extends BaseContract<'rxjs'> {
|
||||
constructor (api: ApiRx, abi: AnyJson | Abi, address: string | AccountId) {
|
||||
super(api, abi, address, decorateMethod);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2017-2020 @polkadot/api-contract authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { default as Blueprint } from './Blueprint';
|
||||
export { default as Code } from './Code';
|
||||
export { default as Contract } from './Contract';
|
||||
export { Blueprint } from './Blueprint';
|
||||
export { Code } from './Code';
|
||||
export { Contract } from './Contract';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Codec, CodecArg, TypeDef } from '@polkadot/types/types';
|
||||
import ApiBase from '@polkadot/api/base';
|
||||
import { Text, u64 } from '@polkadot/types';
|
||||
|
||||
import Abi from './Abi';
|
||||
import { Abi } from '.';
|
||||
|
||||
export interface ContractBase<ApiType extends ApiTypes> {
|
||||
readonly abi: Abi;
|
||||
|
||||
@@ -33,14 +33,14 @@
|
||||
"@polkadot/api": "2.6.2-0",
|
||||
"@polkadot/rpc-core": "2.6.2-0",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util-crypto": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"@polkadot/util-crypto": "^4.0.2-5",
|
||||
"bn.js": "^5.1.3",
|
||||
"memoizee": "^0.4.14",
|
||||
"rxjs": "^6.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^4.0.1",
|
||||
"@polkadot/keyring": "^4.0.2-5",
|
||||
"@polkadot/rpc-provider": "2.6.2-0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
|
||||
import { Observable, from } from 'rxjs';
|
||||
import ApiRx from '@polkadot/api/rx/Api';
|
||||
import { ApiRx } from '@polkadot/api/rx';
|
||||
import MockProvider from '@polkadot/rpc-provider/mock';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ function injectFunctions<AllSections> (instanceId: string, api: ApiInterfaceRx,
|
||||
// FIXME The return type of this function should be {...ExactDerive, ...DeriveCustom}
|
||||
// For now we just drop the custom derive typings
|
||||
/** @internal */
|
||||
export default function decorateDerive (instanceId: string, api: ApiInterfaceRx, custom: DeriveCustom = {}): ExactDerive {
|
||||
export function decorateDerive (instanceId: string, api: ApiInterfaceRx, custom: DeriveCustom = {}): ExactDerive {
|
||||
return {
|
||||
...injectFunctions(instanceId, api, derive),
|
||||
...injectFunctions(instanceId, api, custom)
|
||||
|
||||
@@ -30,14 +30,14 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@polkadot/api-derive": "2.6.2-0",
|
||||
"@polkadot/keyring": "^4.0.1",
|
||||
"@polkadot/keyring": "^4.0.2-5",
|
||||
"@polkadot/metadata": "2.6.2-0",
|
||||
"@polkadot/rpc-core": "2.6.2-0",
|
||||
"@polkadot/rpc-provider": "2.6.2-0",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/types-known": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util-crypto": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"@polkadot/util-crypto": "^4.0.2-5",
|
||||
"bn.js": "^5.1.3",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"rxjs": "^6.6.3"
|
||||
|
||||
@@ -11,12 +11,12 @@ import { ApiInterfaceRx, ApiOptions, ApiTypes, DecorateMethod, DecoratedRpc, Dec
|
||||
import BN from 'bn.js';
|
||||
import { BehaviorSubject, Observable, combineLatest, of } from 'rxjs';
|
||||
import { map, switchMap, take, tap, toArray } from 'rxjs/operators';
|
||||
import decorateDerive, { ExactDerive } from '@polkadot/api-derive';
|
||||
import { ExactDerive, decorateDerive } from '@polkadot/api-derive';
|
||||
import { memo } from '@polkadot/api-derive/util';
|
||||
import { expandMetadata } from '@polkadot/metadata';
|
||||
import RpcCore from '@polkadot/rpc-core';
|
||||
import { Metadata, expandMetadata } from '@polkadot/metadata';
|
||||
import { RpcCore } from '@polkadot/rpc-core';
|
||||
import { WsProvider } from '@polkadot/rpc-provider';
|
||||
import { Metadata, Option, Raw, Text, TypeRegistry, u64 } from '@polkadot/types';
|
||||
import { Option, Raw, Text, TypeRegistry, u64 } from '@polkadot/types';
|
||||
import { DEFAULT_VERSION as EXTRINSIC_DEFAULT_VERSION } from '@polkadot/types/extrinsic/constants';
|
||||
import StorageKey, { StorageEntry, unwrapStorageType } from '@polkadot/types/primitive/StorageKey';
|
||||
import { assert, compactStripLength, logger, u8aToHex } from '@polkadot/util';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { IExtrinsic, IMethod } from '@polkadot/types/types';
|
||||
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { HeaderExtended } from '@polkadot/api-derive';
|
||||
import testKeyring, { TestKeyringMap } from '@polkadot/keyring/testingPairs';
|
||||
import { TestKeyringMap, createTestPairs } from '@polkadot/keyring/testingPairs';
|
||||
import { createTypeUnsafe, TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import { SubmittableResult } from './';
|
||||
@@ -183,7 +183,7 @@ async function tx (api: ApiPromise, keyring: TestKeyringMap): Promise<void> {
|
||||
|
||||
async function main (): Promise<void> {
|
||||
const api = await ApiPromise.create();
|
||||
const keyring = testKeyring();
|
||||
const keyring = createTestPairs();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
Promise.all([
|
||||
|
||||
@@ -8,8 +8,9 @@ import { detectPackage } from '@polkadot/util';
|
||||
detectPackage(require('./package.json'), typeof __dirname !== 'undefined' && __dirname);
|
||||
|
||||
export { Keyring } from '@polkadot/keyring';
|
||||
export { default as WsProvider } from '@polkadot/rpc-provider/ws';
|
||||
export { WsProvider } from '@polkadot/rpc-provider/ws';
|
||||
|
||||
export { default as ApiPromise } from './promise';
|
||||
export { default as ApiRx } from './rx';
|
||||
export * from './submittable';
|
||||
export { ApiPromise } from './promise';
|
||||
export { ApiRx } from './rx';
|
||||
|
||||
export { SubmittableResult } from './submittable';
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { Callback, Codec } from '@polkadot/types/types';
|
||||
import { ApiOptions, DecorateFn, DecorateMethodOptions, ObsInnerType, StorageEntryPromiseOverloads, UnsubscribePromise, VoidFn } from '../types';
|
||||
|
||||
import { EMPTY, Observable, Subscription } from 'rxjs';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
import { isFunction, assert } from '@polkadot/util';
|
||||
|
||||
import ApiBase from '../base';
|
||||
import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator';
|
||||
|
||||
interface Tracker<T> {
|
||||
reject: (value: Error) => Observable<never>;
|
||||
resolve: (value: T) => void;
|
||||
}
|
||||
|
||||
// extract the arguments and callback params from a value array possibly containing a callback
|
||||
function extractArgs (args: unknown[], needsCallback: boolean): [unknown[], Callback<Codec> | undefined] {
|
||||
let callback: Callback<Codec> | 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() as Callback<Codec>;
|
||||
}
|
||||
|
||||
// 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 an isComplete variable that ensures the promise only resolves once
|
||||
function promiseTracker<T> (resolve: (value: T) => void, reject: (value: Error) => void): Tracker<T> {
|
||||
let isCompleted = false;
|
||||
|
||||
return {
|
||||
reject: (error: Error): Observable<never> => {
|
||||
if (!isCompleted) {
|
||||
isCompleted = true;
|
||||
|
||||
reject(error);
|
||||
}
|
||||
|
||||
return EMPTY;
|
||||
},
|
||||
resolve: (value: T): void => {
|
||||
if (!isCompleted) {
|
||||
isCompleted = true;
|
||||
|
||||
resolve(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Decorate a call for a single-shot result - retrieve and then immediate unsubscribe
|
||||
function decorateCall<Method extends DecorateFn<ObsInnerType<ReturnType<Method>>>> (method: Method, actualArgs: unknown[]): Promise<ObsInnerType<ReturnType<Method>>> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
// single result tracker - either reject with Error or resolve with Codec result
|
||||
const tracker = promiseTracker(resolve, reject);
|
||||
|
||||
// encoding errors reject immediately, any result unsubscribes and resolves
|
||||
const subscription: Subscription = method(...actualArgs).pipe(
|
||||
catchError((error) => tracker.reject(error))
|
||||
).subscribe((result): void => {
|
||||
tracker.resolve(result);
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Decorate a subscription where we have a result callback specified
|
||||
function decorateSubscribe<Method extends DecorateFn<ObsInnerType<ReturnType<Method>>>> (method: Method, actualArgs: unknown[], resultCb: Callback<Codec>): UnsubscribePromise {
|
||||
return new Promise<VoidFn>((resolve, reject): void => {
|
||||
// either reject with error or resolve with unsubscribe callback
|
||||
const tracker = promiseTracker(resolve, reject);
|
||||
|
||||
// errors reject immediately, the first result resolves with an unsubscribe promise, all results via callback
|
||||
const subscription: Subscription = method(...actualArgs).pipe(
|
||||
catchError((error) => tracker.reject(error)),
|
||||
tap(() => tracker.resolve(() => subscription.unsubscribe()))
|
||||
).subscribe((result): void => {
|
||||
// queue result (back of queue to clear current)
|
||||
setTimeout(() => resultCb(result) as void, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Decorate method for ApiPromise, where the results are converted to the Promise equivalent
|
||||
*/
|
||||
export function decorateMethod<Method extends DecorateFn<ObsInnerType<ReturnType<Method>>>> (method: Method, options?: DecorateMethodOptions): StorageEntryPromiseOverloads {
|
||||
const needsCallback = options && options.methodName && options.methodName.includes('subscribe');
|
||||
|
||||
return function (...args: unknown[]): Promise<ObsInnerType<ReturnType<Method>>> | UnsubscribePromise {
|
||||
const [actualArgs, resultCb] = extractArgs(args, !!needsCallback);
|
||||
|
||||
return resultCb
|
||||
? decorateSubscribe(method, actualArgs, resultCb)
|
||||
: decorateCall((options?.overrideNoSub as Method) || method, actualArgs);
|
||||
} as StorageEntryPromiseOverloads;
|
||||
}
|
||||
|
||||
/**
|
||||
* # @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.
|
||||
*
|
||||
* The API is well suited to real-time applications where either the single-shot state is needed or use is to be made of the subscription-based features of Polkadot (and Substrate) clients.
|
||||
*
|
||||
* @see [[ApiRx]]
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* Making rpc calls -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import ApiPromise from '@polkadot/api/promise';
|
||||
*
|
||||
* // initialise via static create
|
||||
* const api = await ApiPromise.create();
|
||||
*
|
||||
* // make a subscription to the network head
|
||||
* api.rpc.chain.subscribeNewHeads((header) => {
|
||||
* console.log(`Chain is at #${header.number}`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Subscribing to chain state -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { ApiPromise, WsProvider } from '@polkadot/api';
|
||||
*
|
||||
* // initialise a provider with a specific endpoint
|
||||
* const provider = new WsProvider('wss://example.com:9944')
|
||||
*
|
||||
* // initialise via isReady & new with specific provider
|
||||
* const api = await new ApiPromise({ provider }).isReady;
|
||||
*
|
||||
* // retrieve the block target time
|
||||
* const blockPeriod = await api.query.timestamp.blockPeriod().toNumber();
|
||||
* let last = 0;
|
||||
*
|
||||
* // subscribe to the current block timestamp, updates automatically (callback provided)
|
||||
* api.query.timestamp.now((timestamp) => {
|
||||
* const elapsed = last
|
||||
* ? `, ${timestamp.toNumber() - last}s since last`
|
||||
* : '';
|
||||
*
|
||||
* last = timestamp.toNumber();
|
||||
* console.log(`timestamp ${timestamp}${elapsed} (${blockPeriod}s target)`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Submitting a transaction -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import ApiPromise from '@polkadot/api/promise';
|
||||
*
|
||||
* ApiPromise.create().then((api) => {
|
||||
* const [nonce] = await api.query.system.account(keyring.alice.address);
|
||||
*
|
||||
* api.tx.balances
|
||||
* // create transfer
|
||||
* transfer(keyring.bob.address, 12345)
|
||||
* // sign the transcation
|
||||
* .sign(keyring.alice, { nonce })
|
||||
* // send the transaction (optional status callback)
|
||||
* .send((status) => {
|
||||
* console.log(`current status ${status.type}`);
|
||||
* })
|
||||
* // retrieve the submitted extrinsic hash
|
||||
* .then((hash) => {
|
||||
* console.log(`submitted with hash ${hash}`);
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export default class ApiPromise extends ApiBase<'promise'> {
|
||||
#isReadyPromise: Promise<ApiPromise>;
|
||||
#isReadyOrErrorPromise: Promise<ApiPromise>;
|
||||
|
||||
/**
|
||||
* @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
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import Api from '@polkadot/api/promise';
|
||||
*
|
||||
* Api.create().then(async (api) => {
|
||||
* const timestamp = await api.query.timestamp.now();
|
||||
*
|
||||
* console.log(`lastest block timestamp ${timestamp}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public static create (options?: ApiOptions): Promise<ApiPromise> {
|
||||
return new ApiPromise(options).isReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates an instance of the ApiPromise class
|
||||
* @param options Options to create an instance. This can be either [[ApiOptions]] or
|
||||
* an [[WsProvider]].
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import Api from '@polkadot/api/promise';
|
||||
*
|
||||
* new Api().isReady.then((api) => {
|
||||
* api.rpc.subscribeNewHeads((header) => {
|
||||
* console.log(`new block #${header.number.toNumber()}`);
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
constructor (options?: ApiOptions) {
|
||||
super(options, 'promise', decorateMethod);
|
||||
|
||||
this.#isReadyPromise = new Promise((resolve): void => {
|
||||
super.once('ready', (): void => {
|
||||
resolve(this);
|
||||
});
|
||||
});
|
||||
|
||||
this.#isReadyOrErrorPromise = new Promise((resolve, reject): void => {
|
||||
super.once('ready', (): void => {
|
||||
resolve(this);
|
||||
});
|
||||
super.once('error', (e): void => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Promise that resolves the first time we are connected and loaded
|
||||
*/
|
||||
public get isReady (): Promise<ApiPromise> {
|
||||
return this.#isReadyPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Promise that resolves if we can connect, or reject if there is an error
|
||||
*/
|
||||
public get isReadyOrError (): Promise<ApiPromise> {
|
||||
return this.#isReadyOrErrorPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of this ApiPromise instance (new underlying provider connection)
|
||||
*/
|
||||
public clone (): ApiPromise {
|
||||
return new ApiPromise({
|
||||
...this._options,
|
||||
source: this
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates a combinator that can be used to combine the latest results from multiple subscriptions
|
||||
* @param fns An array of function to combine, each in the form of `(cb: (value: void)) => void`
|
||||
* @param callback A callback that will return an Array of all the values this combinator has been applied to
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* const address = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7';
|
||||
*
|
||||
* // combines values from balance & nonce as it updates
|
||||
* api.combineLatest([
|
||||
* api.rpc.chain.subscribeNewHeads,
|
||||
* (cb) => api.query.system.account(address, cb)
|
||||
* ], ([head, [balance, nonce]]) => {
|
||||
* console.log(`#${head.number}: You have ${balance.free} units, with ${nonce} transactions sent`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async combineLatest <T extends any[] = any[]> (fns: (CombinatorFunction | [CombinatorFunction, ...any[]])[], callback: CombinatorCallback<T>): UnsubscribePromise {
|
||||
const combinator = new Combinator(fns, callback);
|
||||
|
||||
return (): void => {
|
||||
combinator.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export interface CombinatorFunction {
|
||||
(cb: Callback<any>): UnsubscribePromise;
|
||||
}
|
||||
|
||||
export default class Combinator<T extends any[] = any[]> {
|
||||
export class Combinator<T extends any[] = any[]> {
|
||||
#allHasFired = false;
|
||||
|
||||
#callback: CombinatorCallback<T>;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { UnsubscribePromise } from '../types';
|
||||
import Combinator from './Combinator';
|
||||
import { Combinator } from './Combinator';
|
||||
|
||||
describe('Combinator', (): void => {
|
||||
let fns: ((value: any) => void)[] = [];
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
|
||||
import { ApiOptions, SubmittableExtrinsic } from '../types';
|
||||
|
||||
import createPair from '@polkadot/keyring/pair';
|
||||
import testKeyring from '@polkadot/keyring/testing';
|
||||
import { createPair } from '@polkadot/keyring/pair';
|
||||
import { createTestKeyring } from '@polkadot/keyring/testing';
|
||||
import Mock from '@polkadot/rpc-provider/mock/index';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
|
||||
import { SingleAccountSigner } from '../../test/util';
|
||||
import ApiPromise from './Api';
|
||||
import { ApiPromise } from '.';
|
||||
|
||||
const TRANSFER_SIG = '0x7a10e5ed9a14284eca7bea53f81631981dddda5a3d2dee973b136475947264801465726e4829ae3994d9058df638d959d4e043c7f1924299546790dda1dea20a';
|
||||
|
||||
describe('ApiPromise', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
const keyring = testKeyring({ type: 'ed25519' });
|
||||
const keyring = createTestKeyring({ type: 'ed25519' });
|
||||
const aliceEd = keyring.addPair(
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
createPair({ toSS58: keyring.encodeAddress, type: 'ed25519' }, {
|
||||
@@ -1,7 +1,304 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import ApiPromise, { decorateMethod } from './Api';
|
||||
import { Callback, Codec } from '@polkadot/types/types';
|
||||
import { ApiOptions, DecorateFn, DecorateMethodOptions, ObsInnerType, StorageEntryPromiseOverloads, UnsubscribePromise, VoidFn } from '../types';
|
||||
|
||||
export default ApiPromise;
|
||||
export { decorateMethod };
|
||||
import { EMPTY, Observable, Subscription } from 'rxjs';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
import { isFunction, assert } from '@polkadot/util';
|
||||
|
||||
import ApiBase from '../base';
|
||||
import { Combinator, CombinatorCallback, CombinatorFunction } from './Combinator';
|
||||
|
||||
interface Tracker<T> {
|
||||
reject: (value: Error) => Observable<never>;
|
||||
resolve: (value: T) => void;
|
||||
}
|
||||
|
||||
// extract the arguments and callback params from a value array possibly containing a callback
|
||||
function extractArgs (args: unknown[], needsCallback: boolean): [unknown[], Callback<Codec> | undefined] {
|
||||
let callback: Callback<Codec> | 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() as Callback<Codec>;
|
||||
}
|
||||
|
||||
// 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 an isComplete variable that ensures the promise only resolves once
|
||||
function promiseTracker<T> (resolve: (value: T) => void, reject: (value: Error) => void): Tracker<T> {
|
||||
let isCompleted = false;
|
||||
|
||||
return {
|
||||
reject: (error: Error): Observable<never> => {
|
||||
if (!isCompleted) {
|
||||
isCompleted = true;
|
||||
|
||||
reject(error);
|
||||
}
|
||||
|
||||
return EMPTY;
|
||||
},
|
||||
resolve: (value: T): void => {
|
||||
if (!isCompleted) {
|
||||
isCompleted = true;
|
||||
|
||||
resolve(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Decorate a call for a single-shot result - retrieve and then immediate unsubscribe
|
||||
function decorateCall<Method extends DecorateFn<ObsInnerType<ReturnType<Method>>>> (method: Method, actualArgs: unknown[]): Promise<ObsInnerType<ReturnType<Method>>> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
// single result tracker - either reject with Error or resolve with Codec result
|
||||
const tracker = promiseTracker(resolve, reject);
|
||||
|
||||
// encoding errors reject immediately, any result unsubscribes and resolves
|
||||
const subscription: Subscription = method(...actualArgs).pipe(
|
||||
catchError((error) => tracker.reject(error))
|
||||
).subscribe((result): void => {
|
||||
tracker.resolve(result);
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Decorate a subscription where we have a result callback specified
|
||||
function decorateSubscribe<Method extends DecorateFn<ObsInnerType<ReturnType<Method>>>> (method: Method, actualArgs: unknown[], resultCb: Callback<Codec>): UnsubscribePromise {
|
||||
return new Promise<VoidFn>((resolve, reject): void => {
|
||||
// either reject with error or resolve with unsubscribe callback
|
||||
const tracker = promiseTracker(resolve, reject);
|
||||
|
||||
// errors reject immediately, the first result resolves with an unsubscribe promise, all results via callback
|
||||
const subscription: Subscription = method(...actualArgs).pipe(
|
||||
catchError((error) => tracker.reject(error)),
|
||||
tap(() => tracker.resolve(() => subscription.unsubscribe()))
|
||||
).subscribe((result): void => {
|
||||
// queue result (back of queue to clear current)
|
||||
setTimeout(() => resultCb(result) as void, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Decorate method for ApiPromise, where the results are converted to the Promise equivalent
|
||||
*/
|
||||
export function decorateMethod<Method extends DecorateFn<ObsInnerType<ReturnType<Method>>>> (method: Method, options?: DecorateMethodOptions): StorageEntryPromiseOverloads {
|
||||
const needsCallback = options && options.methodName && options.methodName.includes('subscribe');
|
||||
|
||||
return function (...args: unknown[]): Promise<ObsInnerType<ReturnType<Method>>> | UnsubscribePromise {
|
||||
const [actualArgs, resultCb] = extractArgs(args, !!needsCallback);
|
||||
|
||||
return resultCb
|
||||
? decorateSubscribe(method, actualArgs, resultCb)
|
||||
: decorateCall((options?.overrideNoSub as Method) || method, actualArgs);
|
||||
} as StorageEntryPromiseOverloads;
|
||||
}
|
||||
|
||||
/**
|
||||
* # @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.
|
||||
*
|
||||
* The API is well suited to real-time applications where either the single-shot state is needed or use is to be made of the subscription-based features of Polkadot (and Substrate) clients.
|
||||
*
|
||||
* @see [[ApiRx]]
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* Making rpc calls -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import ApiPromise from '@polkadot/api/promise';
|
||||
*
|
||||
* // initialise via static create
|
||||
* const api = await ApiPromise.create();
|
||||
*
|
||||
* // make a subscription to the network head
|
||||
* api.rpc.chain.subscribeNewHeads((header) => {
|
||||
* console.log(`Chain is at #${header.number}`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Subscribing to chain state -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { ApiPromise, WsProvider } from '@polkadot/api';
|
||||
*
|
||||
* // initialise a provider with a specific endpoint
|
||||
* const provider = new WsProvider('wss://example.com:9944')
|
||||
*
|
||||
* // initialise via isReady & new with specific provider
|
||||
* const api = await new ApiPromise({ provider }).isReady;
|
||||
*
|
||||
* // retrieve the block target time
|
||||
* const blockPeriod = await api.query.timestamp.blockPeriod().toNumber();
|
||||
* let last = 0;
|
||||
*
|
||||
* // subscribe to the current block timestamp, updates automatically (callback provided)
|
||||
* api.query.timestamp.now((timestamp) => {
|
||||
* const elapsed = last
|
||||
* ? `, ${timestamp.toNumber() - last}s since last`
|
||||
* : '';
|
||||
*
|
||||
* last = timestamp.toNumber();
|
||||
* console.log(`timestamp ${timestamp}${elapsed} (${blockPeriod}s target)`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Submitting a transaction -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import ApiPromise from '@polkadot/api/promise';
|
||||
*
|
||||
* ApiPromise.create().then((api) => {
|
||||
* const [nonce] = await api.query.system.account(keyring.alice.address);
|
||||
*
|
||||
* api.tx.balances
|
||||
* // create transfer
|
||||
* transfer(keyring.bob.address, 12345)
|
||||
* // sign the transcation
|
||||
* .sign(keyring.alice, { nonce })
|
||||
* // send the transaction (optional status callback)
|
||||
* .send((status) => {
|
||||
* console.log(`current status ${status.type}`);
|
||||
* })
|
||||
* // retrieve the submitted extrinsic hash
|
||||
* .then((hash) => {
|
||||
* console.log(`submitted with hash ${hash}`);
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class ApiPromise extends ApiBase<'promise'> {
|
||||
#isReadyPromise: Promise<ApiPromise>;
|
||||
#isReadyOrErrorPromise: Promise<ApiPromise>;
|
||||
|
||||
/**
|
||||
* @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
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import Api from '@polkadot/api/promise';
|
||||
*
|
||||
* Api.create().then(async (api) => {
|
||||
* const timestamp = await api.query.timestamp.now();
|
||||
*
|
||||
* console.log(`lastest block timestamp ${timestamp}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public static create (options?: ApiOptions): Promise<ApiPromise> {
|
||||
return new ApiPromise(options).isReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates an instance of the ApiPromise class
|
||||
* @param options Options to create an instance. This can be either [[ApiOptions]] or
|
||||
* an [[WsProvider]].
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import Api from '@polkadot/api/promise';
|
||||
*
|
||||
* new Api().isReady.then((api) => {
|
||||
* api.rpc.subscribeNewHeads((header) => {
|
||||
* console.log(`new block #${header.number.toNumber()}`);
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
constructor (options?: ApiOptions) {
|
||||
super(options, 'promise', decorateMethod);
|
||||
|
||||
this.#isReadyPromise = new Promise((resolve): void => {
|
||||
super.once('ready', (): void => {
|
||||
resolve(this);
|
||||
});
|
||||
});
|
||||
|
||||
this.#isReadyOrErrorPromise = new Promise((resolve, reject): void => {
|
||||
super.once('ready', (): void => {
|
||||
resolve(this);
|
||||
});
|
||||
super.once('error', (e): void => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Promise that resolves the first time we are connected and loaded
|
||||
*/
|
||||
public get isReady (): Promise<ApiPromise> {
|
||||
return this.#isReadyPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Promise that resolves if we can connect, or reject if there is an error
|
||||
*/
|
||||
public get isReadyOrError (): Promise<ApiPromise> {
|
||||
return this.#isReadyOrErrorPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of this ApiPromise instance (new underlying provider connection)
|
||||
*/
|
||||
public clone (): ApiPromise {
|
||||
return new ApiPromise({
|
||||
...this._options,
|
||||
source: this
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates a combinator that can be used to combine the latest results from multiple subscriptions
|
||||
* @param fns An array of function to combine, each in the form of `(cb: (value: void)) => void`
|
||||
* @param callback A callback that will return an Array of all the values this combinator has been applied to
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* const address = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7';
|
||||
*
|
||||
* // combines values from balance & nonce as it updates
|
||||
* api.combineLatest([
|
||||
* api.rpc.chain.subscribeNewHeads,
|
||||
* (cb) => api.query.system.account(address, cb)
|
||||
* ], ([head, [balance, nonce]]) => {
|
||||
* console.log(`#${head.number}: You have ${balance.free} units, with ${nonce} transactions sent`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async combineLatest <T extends any[] = any[]> (fns: (CombinatorFunction | [CombinatorFunction, ...any[]])[], callback: CombinatorCallback<T>): UnsubscribePromise {
|
||||
const combinator = new Combinator(fns, callback);
|
||||
|
||||
return (): void => {
|
||||
combinator.unsubscribe();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { Codec } from '@polkadot/types/types';
|
||||
import { ApiOptions, DecorateFn } from '../types';
|
||||
|
||||
import { from, Observable } from 'rxjs';
|
||||
|
||||
import ApiBase from '../base';
|
||||
|
||||
export function decorateMethod <Method extends DecorateFn<Codec>> (method: Method): Method {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* # @polkadot/api/rx
|
||||
*
|
||||
* ## Overview
|
||||
*
|
||||
* @name ApiRx
|
||||
*
|
||||
* @description
|
||||
* ApiRx is a powerful RxJS Observable wrapper around the RPC and interfaces on the Polkadot network. As a full Observable API, all interface calls return RxJS Observables, including the static `.create(...)`. In the same fashion and subscription-based methods return long-running Observables that update with the latest values.
|
||||
*
|
||||
* The API is well suited to real-time applications where the latest state is needed, unlocking the subscription-based features of Polkadot (and Substrate) clients. Some familiarity with RxJS is a requirement to use the API, however just understanding `.subscribe` and `.pipe` on Observables will unlock full-scale use thereof.
|
||||
*
|
||||
* @see [[ApiPromise]]
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* Making rpc calls -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import ApiRx from '@polkadot/api/rx';
|
||||
*
|
||||
* // initialize via Promise & static create
|
||||
* const api = await ApiRx.create().toPromise();
|
||||
*
|
||||
* // make a call to retrieve the current network head
|
||||
* api.rpc.chain.subscribeNewHeads().subscribe((header) => {
|
||||
* console.log(`Chain is at #${header.number}`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Subscribing to chain state -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { combineLatest } from 'rxjs';
|
||||
* import { pairwise, switchMap } from 'rxjs/operators';
|
||||
* import { ApiRx, WsProvider } from '@polkadot/api';
|
||||
*
|
||||
*
|
||||
* // initialize a provider with a specific endpoint
|
||||
* const provider = new WsProvider('wss://example.com:9944')
|
||||
*
|
||||
* // initialize via isReady & new with specific provider
|
||||
* new ApiRx({ provider })
|
||||
* .isReady
|
||||
* .pipe(
|
||||
* switchMap((api) =>
|
||||
* combineLatest([
|
||||
* api.query.timestamp.blockPeriod(),
|
||||
* api.query.timestamp.now().pipe(pairwise())
|
||||
* ])
|
||||
* )
|
||||
* )
|
||||
* .subscribe(([blockPeriod, timestamp]) => {
|
||||
* const elapsed = timestamp[1].toNumber() - timestamp[0].toNumber();
|
||||
* console.log(`timestamp ${timestamp[1]} \nelapsed ${elapsed} \n(${blockPeriod}s target)`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Submitting a transaction -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { first, switchMap } from 'rxjs/operators';
|
||||
* import ApiRx from '@polkadot/api/rx';
|
||||
*
|
||||
* // import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
|
||||
* import testingPairs from '@polkadot/keyring/testingPairs';
|
||||
* const keyring = testingPairs();
|
||||
*
|
||||
* // get api via Promise
|
||||
* const api = await ApiRx.create().toPromise();
|
||||
*
|
||||
* // retrieve nonce for the account
|
||||
* api.query.system
|
||||
* .account(keyring.alice.address)
|
||||
* .pipe(
|
||||
* first(),
|
||||
* // pipe nonce into transfer
|
||||
* switchMap(([nonce]) =>
|
||||
* api.tx.balances
|
||||
* // create transfer
|
||||
* .transfer(keyring.bob.address, 12345)
|
||||
* // sign the transaction
|
||||
* .sign(keyring.alice, { nonce })
|
||||
* // send the transaction
|
||||
* .send()
|
||||
* )
|
||||
* )
|
||||
* // subscribe to overall result
|
||||
* .subscribe(({ status }) => {
|
||||
* if (status.isInBlock) {
|
||||
* console.log('Completed at block hash', status.asFinalized.toHex());
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export default class ApiRx extends ApiBase<'rxjs'> {
|
||||
#isReadyRx: Observable<ApiRx>;
|
||||
|
||||
/**
|
||||
* @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 constructor. Can be either [[ApiOptions]] or [[WsProvider]]
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { switchMap } from 'rxjs/operators';
|
||||
* import Api from '@polkadot/api/rx';
|
||||
*
|
||||
* Api.create()
|
||||
* .pipe(
|
||||
* switchMap((api) =>
|
||||
* api.rpc.chain.subscribeNewHeads()
|
||||
* ))
|
||||
* .subscribe((header) => {
|
||||
* console.log(`new block #${header.number.toNumber()}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public static create (options?: ApiOptions): Observable<ApiRx> {
|
||||
return new ApiRx(options).isReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create an instance of the ApiRx class
|
||||
* @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]]
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { switchMap } from 'rxjs/operators';
|
||||
* import Api from '@polkadot/api/rx';
|
||||
*
|
||||
* new Api().isReady
|
||||
* .pipe(
|
||||
* switchMap((api) =>
|
||||
* api.rpc.chain.subscribeNewHeads()
|
||||
* ))
|
||||
* .subscribe((header) => {
|
||||
* console.log(`new block #${header.number.toNumber()}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
constructor (options?: ApiOptions) {
|
||||
super(options, 'rxjs', decorateMethod);
|
||||
|
||||
this.#isReadyRx = from<Promise<ApiRx>>(
|
||||
// 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);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Observable that returns the first time we are connected and loaded
|
||||
*/
|
||||
public get isReady (): Observable<ApiRx> {
|
||||
return this.#isReadyRx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of this ApiRx instance (new underlying provider connection)
|
||||
*/
|
||||
public clone (): ApiRx {
|
||||
return new ApiRx({
|
||||
...this._options,
|
||||
source: this
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,16 @@ import { SubmittableExtrinsic } from '../submittable/types';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import testingPairs from '@polkadot/keyring/testingPairs';
|
||||
import { createTestPairs } from '@polkadot/keyring/testingPairs';
|
||||
import Mock from '@polkadot/rpc-provider/mock/index';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import { SingleAccountSigner } from '../../test/util';
|
||||
import ApiRx from './Api';
|
||||
import { ApiRx } from '.';
|
||||
|
||||
describe('ApiRx', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
const keyring = createTestPairs({ type: 'ed25519' });
|
||||
let provider: Mock;
|
||||
|
||||
beforeEach((): void => {
|
||||
@@ -1,7 +1,191 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import ApiRx, { decorateMethod } from './Api';
|
||||
import { Codec } from '@polkadot/types/types';
|
||||
import { ApiOptions, DecorateFn } from '../types';
|
||||
|
||||
export default ApiRx;
|
||||
export { decorateMethod };
|
||||
import { from, Observable } from 'rxjs';
|
||||
|
||||
import ApiBase from '../base';
|
||||
|
||||
export function decorateMethod <Method extends DecorateFn<Codec>> (method: Method): Method {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* # @polkadot/api/rx
|
||||
*
|
||||
* ## Overview
|
||||
*
|
||||
* @name ApiRx
|
||||
*
|
||||
* @description
|
||||
* ApiRx is a powerful RxJS Observable wrapper around the RPC and interfaces on the Polkadot network. As a full Observable API, all interface calls return RxJS Observables, including the static `.create(...)`. In the same fashion and subscription-based methods return long-running Observables that update with the latest values.
|
||||
*
|
||||
* The API is well suited to real-time applications where the latest state is needed, unlocking the subscription-based features of Polkadot (and Substrate) clients. Some familiarity with RxJS is a requirement to use the API, however just understanding `.subscribe` and `.pipe` on Observables will unlock full-scale use thereof.
|
||||
*
|
||||
* @see [[ApiPromise]]
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* Making rpc calls -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import ApiRx from '@polkadot/api/rx';
|
||||
*
|
||||
* // initialize via Promise & static create
|
||||
* const api = await ApiRx.create().toPromise();
|
||||
*
|
||||
* // make a call to retrieve the current network head
|
||||
* api.rpc.chain.subscribeNewHeads().subscribe((header) => {
|
||||
* console.log(`Chain is at #${header.number}`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Subscribing to chain state -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { combineLatest } from 'rxjs';
|
||||
* import { pairwise, switchMap } from 'rxjs/operators';
|
||||
* import { ApiRx, WsProvider } from '@polkadot/api';
|
||||
*
|
||||
*
|
||||
* // initialize a provider with a specific endpoint
|
||||
* const provider = new WsProvider('wss://example.com:9944')
|
||||
*
|
||||
* // initialize via isReady & new with specific provider
|
||||
* new ApiRx({ provider })
|
||||
* .isReady
|
||||
* .pipe(
|
||||
* switchMap((api) =>
|
||||
* combineLatest([
|
||||
* api.query.timestamp.blockPeriod(),
|
||||
* api.query.timestamp.now().pipe(pairwise())
|
||||
* ])
|
||||
* )
|
||||
* )
|
||||
* .subscribe(([blockPeriod, timestamp]) => {
|
||||
* const elapsed = timestamp[1].toNumber() - timestamp[0].toNumber();
|
||||
* console.log(`timestamp ${timestamp[1]} \nelapsed ${elapsed} \n(${blockPeriod}s target)`);
|
||||
* });
|
||||
* ```
|
||||
* <BR>
|
||||
*
|
||||
* Submitting a transaction -
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { first, switchMap } from 'rxjs/operators';
|
||||
* import ApiRx from '@polkadot/api/rx';
|
||||
*
|
||||
* // import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
|
||||
* import testingPairs from '@polkadot/keyring/testingPairs';
|
||||
* const keyring = testingPairs();
|
||||
*
|
||||
* // get api via Promise
|
||||
* const api = await ApiRx.create().toPromise();
|
||||
*
|
||||
* // retrieve nonce for the account
|
||||
* api.query.system
|
||||
* .account(keyring.alice.address)
|
||||
* .pipe(
|
||||
* first(),
|
||||
* // pipe nonce into transfer
|
||||
* switchMap(([nonce]) =>
|
||||
* api.tx.balances
|
||||
* // create transfer
|
||||
* .transfer(keyring.bob.address, 12345)
|
||||
* // sign the transaction
|
||||
* .sign(keyring.alice, { nonce })
|
||||
* // send the transaction
|
||||
* .send()
|
||||
* )
|
||||
* )
|
||||
* // subscribe to overall result
|
||||
* .subscribe(({ status }) => {
|
||||
* if (status.isInBlock) {
|
||||
* console.log('Completed at block hash', status.asFinalized.toHex());
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class ApiRx extends ApiBase<'rxjs'> {
|
||||
#isReadyRx: Observable<ApiRx>;
|
||||
|
||||
/**
|
||||
* @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 constructor. Can be either [[ApiOptions]] or [[WsProvider]]
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { switchMap } from 'rxjs/operators';
|
||||
* import Api from '@polkadot/api/rx';
|
||||
*
|
||||
* Api.create()
|
||||
* .pipe(
|
||||
* switchMap((api) =>
|
||||
* api.rpc.chain.subscribeNewHeads()
|
||||
* ))
|
||||
* .subscribe((header) => {
|
||||
* console.log(`new block #${header.number.toNumber()}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public static create (options?: ApiOptions): Observable<ApiRx> {
|
||||
return new ApiRx(options).isReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create an instance of the ApiRx class
|
||||
* @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]]
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import { switchMap } from 'rxjs/operators';
|
||||
* import Api from '@polkadot/api/rx';
|
||||
*
|
||||
* new Api().isReady
|
||||
* .pipe(
|
||||
* switchMap((api) =>
|
||||
* api.rpc.chain.subscribeNewHeads()
|
||||
* ))
|
||||
* .subscribe((header) => {
|
||||
* console.log(`new block #${header.number.toNumber()}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
constructor (options?: ApiOptions) {
|
||||
super(options, 'rxjs', decorateMethod);
|
||||
|
||||
this.#isReadyRx = from<Promise<ApiRx>>(
|
||||
// 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);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Observable that returns the first time we are connected and loaded
|
||||
*/
|
||||
public get isReady (): Observable<ApiRx> {
|
||||
return this.#isReadyRx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of this ApiRx instance (new underlying provider connection)
|
||||
*/
|
||||
public clone (): ApiRx {
|
||||
return new ApiRx({
|
||||
...this._options,
|
||||
source: this
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ function extractInfo (events: EventRecord[] = []): DispatchInfo | undefined {
|
||||
)[0];
|
||||
}
|
||||
|
||||
export default class SubmittableResult implements ISubmittableResult {
|
||||
export class SubmittableResult implements ISubmittableResult {
|
||||
public readonly dispatchError?: DispatchError;
|
||||
|
||||
public readonly dispatchInfo?: DispatchInfo;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { assert, isBn, isFunction, isNumber, isString, isU8a } from '@polkadot/u
|
||||
|
||||
import { filterEvents, isKeyringPair } from '../util';
|
||||
import ApiBase from '../base';
|
||||
import SubmittableResult from './Result';
|
||||
import { SubmittableResult } from './Result';
|
||||
|
||||
interface SubmittableOptions<ApiType extends ApiTypes> {
|
||||
api: ApiInterfaceRx;
|
||||
@@ -24,7 +24,7 @@ interface SubmittableOptions<ApiType extends ApiTypes> {
|
||||
|
||||
const identity = <T> (input: T): T => input;
|
||||
|
||||
export default function createClass <ApiType extends ApiTypes> ({ api, apiType, decorateMethod }: SubmittableOptions<ApiType>): Constructor<SubmittableExtrinsic<ApiType>> {
|
||||
export function createClass <ApiType extends ApiTypes> ({ api, apiType, decorateMethod }: SubmittableOptions<ApiType>): Constructor<SubmittableExtrinsic<ApiType>> {
|
||||
// an instance of the base extrinsic for us to extend
|
||||
const ExtrinsicBase = api.registry.createClass('Extrinsic');
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ import { ApiInterfaceRx, ApiTypes } from '../types';
|
||||
import { SubmittableExtrinsic } from './types';
|
||||
|
||||
import ApiBase from '../base';
|
||||
import createClass from './createClass';
|
||||
import { createClass } from './createClass';
|
||||
|
||||
type Creator<ApiType extends ApiTypes> = (extrinsic: Call | Uint8Array | string) => SubmittableExtrinsic<ApiType>;
|
||||
|
||||
export default function createSubmittable<ApiType extends ApiTypes> (apiType: ApiTypes, api: ApiInterfaceRx, decorateMethod: ApiBase<ApiType>['_decorateMethod']): Creator<ApiType> {
|
||||
export function createSubmittable<ApiType extends ApiTypes> (apiType: ApiTypes, api: ApiInterfaceRx, decorateMethod: ApiBase<ApiType>['_decorateMethod']): Creator<ApiType> {
|
||||
const Submittable = createClass<ApiType>({ api, apiType, decorateMethod });
|
||||
|
||||
return (extrinsic: Call | Extrinsic | Uint8Array | string): SubmittableExtrinsic<ApiType> =>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2017-2020 @polkadot/api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { default as createSubmittable } from './createSubmittable';
|
||||
export { default as SubmittableResult } from './Result';
|
||||
export { createSubmittable } from './createSubmittable';
|
||||
export { SubmittableResult } from './Result';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { Storage } from '@polkadot/metadata/decorate/types';
|
||||
|
||||
import Metadata, { storageFromMeta } from '@polkadot/metadata';
|
||||
import { Metadata, storageFromMeta } from '@polkadot/metadata';
|
||||
import metaStatic from '@polkadot/metadata/static';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/types-known": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util-crypto": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"@polkadot/util-crypto": "^4.0.2-5",
|
||||
"bn.js": "^5.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^4.0.1"
|
||||
"@polkadot/keyring": "^4.0.2-5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import MagicNumber, { MAGIC_NUMBER } from './MagicNumber';
|
||||
import { MagicNumber, MAGIC_NUMBER } from './MagicNumber';
|
||||
|
||||
describe('MagicNumber', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
@@ -9,7 +9,7 @@ import U32 from '@polkadot/types/primitive/U32';
|
||||
|
||||
export const MAGIC_NUMBER = 0x6174656d; // `meta`, reversed for Little Endian encoding
|
||||
|
||||
export default class MagicNumber extends U32 {
|
||||
export class MagicNumber extends U32 {
|
||||
constructor (registry: Registry, value?: AnyNumber) {
|
||||
super(registry, value);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import substrateData from './static';
|
||||
import Metadata from './Metadata';
|
||||
import { Metadata } from './Metadata';
|
||||
|
||||
describe('Metadata', (): void => {
|
||||
it('allows creation from hex with JSON equivalence', (): void => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Registry } from '@polkadot/types/types';
|
||||
|
||||
import { isString, u8aConcat, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import MetadataVersioned from './MetadataVersioned';
|
||||
import { MetadataVersioned } from './MetadataVersioned';
|
||||
|
||||
// magic u32 preceding the version id
|
||||
const VERSION_IDX = 4;
|
||||
@@ -49,7 +49,7 @@ function decodeMetadata (registry: Registry, _value?: Uint8Array | string): Meta
|
||||
* @description
|
||||
* The versioned runtime metadata as a decoded structure
|
||||
*/
|
||||
export default class Metadata extends MetadataVersioned {
|
||||
export class Metadata extends MetadataVersioned {
|
||||
constructor (registry: Registry, value?: Uint8Array | string) {
|
||||
super(registry, decodeMetadata(registry, value));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Registry } from '@polkadot/types/types';
|
||||
import Struct from '@polkadot/types/codec/Struct';
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import MagicNumber from './MagicNumber';
|
||||
import { MagicNumber } from './MagicNumber';
|
||||
import v9ToV10 from './v9/toV10';
|
||||
import v10ToV11 from './v10/toV11';
|
||||
import v11ToV12 from './v11/toV12';
|
||||
@@ -23,7 +23,7 @@ type MetaAsX = 'asV9' | 'asV10' | 'asV11' | 'asV12';
|
||||
* @description
|
||||
* The versioned runtime metadata as a decoded structure
|
||||
*/
|
||||
export default class MetadataVersioned extends Struct {
|
||||
export class MetadataVersioned extends Struct {
|
||||
readonly #converted = new Map<number, MetaMapped>();
|
||||
|
||||
constructor (registry: Registry, value?: unknown) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Metadata, TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import rpcMetadata from '../../static';
|
||||
import rpcMetadataV10 from '../../v10/static';
|
||||
import fromMetadata from './fromMetadata';
|
||||
import { constantsFromMeta } from './fromMetadata';
|
||||
|
||||
function init (meta: string): [Constants, TypeRegistry] {
|
||||
const registry = new TypeRegistry();
|
||||
@@ -15,7 +15,7 @@ function init (meta: string): [Constants, TypeRegistry] {
|
||||
|
||||
registry.setMetadata(metadata);
|
||||
|
||||
return [fromMetadata(registry, metadata), registry];
|
||||
return [constantsFromMeta(registry, metadata), registry];
|
||||
}
|
||||
|
||||
describe('fromMetadata', (): void => {
|
||||
|
||||
@@ -7,10 +7,10 @@ import { Constants, ConstantCodec, ModuleConstants } from '../types';
|
||||
import { createTypeUnsafe } from '@polkadot/types/create';
|
||||
import { hexToU8a, stringCamelCase } from '@polkadot/util';
|
||||
|
||||
import Metadata from '../..';
|
||||
import { Metadata } from '../..';
|
||||
|
||||
/** @internal */
|
||||
export default function fromMetadata (registry: Registry, metadata: Metadata): Constants {
|
||||
export function constantsFromMeta (registry: Registry, metadata: Metadata): Constants {
|
||||
return metadata.asLatest.modules.reduce((result: Constants, moduleMetadata): Constants => {
|
||||
if (moduleMetadata.constants.isEmpty) {
|
||||
return result;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Metadata, TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import json from '../../static';
|
||||
import fromMetadata from './fromMetadata';
|
||||
import { extrinsicsFromMeta } from './fromMetadata';
|
||||
|
||||
// Use the pre-generated metadata
|
||||
const registry = new TypeRegistry();
|
||||
@@ -12,7 +12,7 @@ const metadata = new Metadata(registry, json);
|
||||
|
||||
registry.setMetadata(metadata);
|
||||
|
||||
const newExtrinsics = fromMetadata(registry, metadata);
|
||||
const newExtrinsics = extrinsicsFromMeta(registry, metadata);
|
||||
|
||||
describe('fromMetadata', (): void => {
|
||||
it('should throw if an incorrect number of args is supplied', (): void => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import extrinsics from './';
|
||||
import createUnchecked from './createUnchecked';
|
||||
|
||||
/** @internal */
|
||||
export default function fromMetadata (registry: Registry, metadata: RegistryMetadata): ModulesWithCalls {
|
||||
export function extrinsicsFromMeta (registry: Registry, metadata: RegistryMetadata): ModulesWithCalls {
|
||||
const modules = metadata.asLatest.modules;
|
||||
const isIndexed = modules.some(({ index }) => !index.eqn(255));
|
||||
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import BN from 'bn.js';
|
||||
import testingPairs from '@polkadot/keyring/testingPairs';
|
||||
import { createTestPairs } from '@polkadot/keyring/testingPairs';
|
||||
import { Metadata, TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import metadataStatic from '../../static';
|
||||
import fromMetadata from './fromMetadata';
|
||||
import { extrinsicsFromMeta } from './fromMetadata';
|
||||
|
||||
const keyring = testingPairs({ type: 'ed25519' }, false);
|
||||
const keyring = createTestPairs({ type: 'ed25519' }, false);
|
||||
const registry = new TypeRegistry();
|
||||
const metadata = new Metadata(registry, metadataStatic);
|
||||
|
||||
registry.setMetadata(metadata);
|
||||
|
||||
const extrinsics = fromMetadata(registry, metadata);
|
||||
const extrinsics = extrinsicsFromMeta(registry, metadata);
|
||||
|
||||
describe('extrinsics', (): void => {
|
||||
it('encodes an actual transfer (actual data)', (): void => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
|
||||
import Metadata from '..';
|
||||
import { Metadata } from '..';
|
||||
import json from '../static';
|
||||
import { constantsFromMeta, extrinsicsFromMeta, storageFromMeta } from './';
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@ import { DecoratedMeta } from './types';
|
||||
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import Metadata from '..';
|
||||
import constantsFromMeta from './consts/fromMetadata';
|
||||
import extrinsicsFromMeta from './extrinsics/fromMetadata';
|
||||
import storageFromMeta from './storage/fromMetadata';
|
||||
import { Metadata } from '..';
|
||||
import { constantsFromMeta } from './consts/fromMetadata';
|
||||
import { extrinsicsFromMeta } from './extrinsics/fromMetadata';
|
||||
import { storageFromMeta } from './storage/fromMetadata';
|
||||
|
||||
/**
|
||||
* Expands the metadata by decoration int consts, query and tx sections
|
||||
*/
|
||||
export default function expandMetadata (registry: Registry, value: Metadata): DecoratedMeta {
|
||||
export function expandMetadata (registry: Registry, value: Metadata): DecoratedMeta {
|
||||
assert(value instanceof Metadata, 'You need to pass a valid Metadata instance to Decorated');
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Copyright 2017-2020 @polkadot/metadata authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import testingPairs from '@polkadot/keyring/testingPairs';
|
||||
import { createTestPairs } from '@polkadot/keyring/testingPairs';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
|
||||
import Metadata from '../../';
|
||||
import { Metadata } from '../../';
|
||||
import rpcMetadata from '../../static';
|
||||
import { storageFromMeta } from '../';
|
||||
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
const keyring = createTestPairs({ type: 'ed25519' });
|
||||
|
||||
describe('fromMetadata', (): void => {
|
||||
describe('latest', (): void => {
|
||||
|
||||
@@ -6,12 +6,12 @@ import { ModuleStorage, Storage } from '../types';
|
||||
|
||||
import { stringCamelCase, stringLowerFirst } from '@polkadot/util';
|
||||
|
||||
import Metadata from '../../Metadata';
|
||||
import { Metadata } from '../../Metadata';
|
||||
import createFunction from './createFunction';
|
||||
import getStorage from './storage';
|
||||
|
||||
/** @internal */
|
||||
export default function fromMetadata (registry: Registry, metadata: Metadata): Storage {
|
||||
export function storageFromMeta (registry: Registry, metadata: Metadata): Storage {
|
||||
return metadata.asLatest.modules.reduce((result: Storage, moduleMetadata): Storage => {
|
||||
if (moduleMetadata.storage.isNone) {
|
||||
return result;
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
// Copyright 2017-2020 @polkadot/metadata authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import expandMetadata, { constantsFromMeta, extrinsicsFromMeta, storageFromMeta } from './decorate';
|
||||
import Metadata from './Metadata';
|
||||
|
||||
export default Metadata;
|
||||
|
||||
export { constantsFromMeta, expandMetadata, extrinsicsFromMeta, storageFromMeta };
|
||||
export { Metadata } from './Metadata';
|
||||
export { constantsFromMeta, expandMetadata, extrinsicsFromMeta, storageFromMeta } from './decorate';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MetadataInterface } from '../types';
|
||||
import { unwrapStorageType } from '@polkadot/types/primitive/StorageKey';
|
||||
|
||||
import getUniqTypes from './getUniqTypes';
|
||||
import Metadata from '..';
|
||||
import { Metadata } from '..';
|
||||
|
||||
/** @internal */
|
||||
export function decodeLatestSubstrate<Modules extends Codec> (registry: Registry, version: number, rpcData: string, staticSubstrate: Record<string, unknown>): void {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import staticData from '../static';
|
||||
import Metadata from '..';
|
||||
import { Metadata } from '..';
|
||||
|
||||
describe('toCallsOnly', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
@@ -14,7 +14,7 @@ Initialization -
|
||||
|
||||
```js
|
||||
import Rpc from '@polkadot/rpc-core';
|
||||
import WsProvider from '@polkadot/rpc-provider/ws';
|
||||
import { WsProvider } from '@polkadot/rpc-provider/ws';
|
||||
|
||||
const provider = new WsProvider('http://127.0.0.1:9944');
|
||||
const rpc = new Rpc(provider);
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
"@polkadot/metadata": "2.6.2-0",
|
||||
"@polkadot/rpc-provider": "2.6.2-0",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"memoizee": "^0.4.14",
|
||||
"rxjs": "^6.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^4.0.1"
|
||||
"@polkadot/keyring": "^4.0.2-5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
// Copyright 2017-2020 @polkadot/rpc-core authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import testingPairs from '@polkadot/keyring/testingPairs';
|
||||
import { createTestPairs } from '@polkadot/keyring/testingPairs';
|
||||
import MockProvider from '@polkadot/rpc-provider/mock';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import Rpc from '.';
|
||||
import { RpcCore } from '.';
|
||||
|
||||
describe('Cached Observables', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
let rpc: Rpc;
|
||||
const keyring = testingPairs();
|
||||
let rpc: RpcCore;
|
||||
const keyring = createTestPairs();
|
||||
|
||||
beforeEach((): void => {
|
||||
rpc = new Rpc('123', registry, new MockProvider(registry));
|
||||
rpc = new RpcCore('123', registry, new MockProvider(registry));
|
||||
});
|
||||
|
||||
it('creates a single observable for subscriptions (multiple calls)', (): void => {
|
||||
|
||||
@@ -5,19 +5,19 @@ import MockProvider from '@polkadot/rpc-provider/mock';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
import Rpc from '.';
|
||||
import { RpcCore } from '.';
|
||||
|
||||
describe('Api', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
it('requires a provider with a send method', (): void => {
|
||||
expect(
|
||||
(): Rpc => new Rpc('234', registry, {} as any)
|
||||
() => new RpcCore('234', registry, {} as any)
|
||||
).toThrow(/Expected Provider/);
|
||||
});
|
||||
|
||||
it('allows for the definition of user RPCs', (): void => {
|
||||
const rpc = new Rpc('567', registry, new MockProvider(registry), {
|
||||
const rpc = new RpcCore('567', registry, new MockProvider(registry), {
|
||||
testing: {
|
||||
foo: {
|
||||
description: 'foo',
|
||||
|
||||
@@ -61,13 +61,13 @@ function logErrorMessage (method: string, { params, type }: DefinitionRpc, error
|
||||
*
|
||||
* ```javascript
|
||||
* import Rpc from '@polkadot/rpc-core';
|
||||
* import WsProvider from '@polkadot/rpc-provider/ws';
|
||||
* import { WsProvider } from '@polkadot/rpc-provider/ws';
|
||||
*
|
||||
* const provider = new WsProvider('ws://127.0.0.1:9944');
|
||||
* const rpc = new Rpc(provider);
|
||||
* ```
|
||||
*/
|
||||
export default class Rpc implements RpcInterface {
|
||||
export class RpcCore implements RpcInterface {
|
||||
#instanceId: string;
|
||||
|
||||
#registryDefault: Registry;
|
||||
|
||||
@@ -5,11 +5,11 @@ import { DefinitionRpc } from '@polkadot/types/types';
|
||||
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import Rpc from '.';
|
||||
import { RpcCore } from '.';
|
||||
|
||||
describe('methodSend', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
let rpc: Rpc;
|
||||
let rpc: RpcCore;
|
||||
let methods: Record<string, DefinitionRpc>;
|
||||
let provider: any;
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('methodSend', (): void => {
|
||||
})
|
||||
};
|
||||
|
||||
rpc = new Rpc('987', registry, provider);
|
||||
rpc = new RpcCore('987', registry, provider);
|
||||
});
|
||||
|
||||
it('checks for mismatched parameters', (done): void => {
|
||||
|
||||
@@ -5,14 +5,14 @@ import { Observable, of } from 'rxjs';
|
||||
import MockProvider from '@polkadot/rpc-provider/mock';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import Rpc from '.';
|
||||
import { RpcCore } from '.';
|
||||
|
||||
describe('replay', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
let rpc: Rpc;
|
||||
let rpc: RpcCore;
|
||||
|
||||
beforeEach((): void => {
|
||||
rpc = new Rpc('653', registry, new MockProvider(registry));
|
||||
rpc = new RpcCore('653', registry, new MockProvider(registry));
|
||||
});
|
||||
|
||||
it('subscribes via the rpc section', (done): void => {
|
||||
|
||||
@@ -19,7 +19,7 @@ yarn add @polkadot/rpc-provider
|
||||
WebSocket Initialization -
|
||||
|
||||
```javascript
|
||||
import WsProvider from '@polkadot/rpc-provider/ws';
|
||||
import { WsProvider } from '@polkadot/rpc-provider/ws';
|
||||
|
||||
// this is the actual default endpoint
|
||||
const provider = new WsProvider('ws://127.0.0.1:9944');
|
||||
|
||||
@@ -30,15 +30,15 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util-crypto": "^4.0.1",
|
||||
"@polkadot/x-fetch": "^4.0.1",
|
||||
"@polkadot/x-ws": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"@polkadot/util-crypto": "^4.0.2-5",
|
||||
"@polkadot/x-fetch": "^4.0.2-5",
|
||||
"@polkadot/x-ws": "^4.0.2-5",
|
||||
"bn.js": "^5.1.3",
|
||||
"eventemitter3": "^4.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^4.0.1",
|
||||
"@polkadot/keyring": "^4.0.2-5",
|
||||
"@polkadot/metadata": "2.6.2-0",
|
||||
"mock-socket": "^9.0.3",
|
||||
"nock": "^13.0.4"
|
||||
|
||||
@@ -2,30 +2,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { TEST_HTTP_URL } from '../../test/mockHttp';
|
||||
import Http from './';
|
||||
import { HttpProvider } from './';
|
||||
|
||||
describe('Http', (): void => {
|
||||
let http: Http;
|
||||
let http: HttpProvider;
|
||||
|
||||
beforeEach((): void => {
|
||||
http = new Http(TEST_HTTP_URL);
|
||||
http = new HttpProvider(TEST_HTTP_URL);
|
||||
});
|
||||
|
||||
it('requires an http:// prefixed endpoint', (): void => {
|
||||
expect(
|
||||
(): Http => new Http('ws://')
|
||||
() => new HttpProvider('ws://')
|
||||
).toThrow(/with 'http/);
|
||||
});
|
||||
|
||||
it('allows https:// endpoints', (): void => {
|
||||
expect(
|
||||
(): Http => new Http('https://')
|
||||
() => new HttpProvider('https://')
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('allows custom headers', (): void => {
|
||||
expect(
|
||||
(): Http => new Http('https://', { foo: 'bar' })
|
||||
() => new HttpProvider('https://', { foo: 'bar' })
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const l = logger('api-http');
|
||||
*
|
||||
* @see [[WsProvider]]
|
||||
*/
|
||||
export default class HttpProvider implements ProviderInterface {
|
||||
export class HttpProvider implements ProviderInterface {
|
||||
readonly #coder: Coder;
|
||||
|
||||
readonly #endpoint: string;
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
import { Mock } from './../mock/types';
|
||||
import { mockHttp, TEST_HTTP_URL } from '../../test/mockHttp';
|
||||
|
||||
import Http from './';
|
||||
import { HttpProvider } from './';
|
||||
|
||||
describe('send', (): void => {
|
||||
let http: Http;
|
||||
let http: HttpProvider;
|
||||
let mock: Mock;
|
||||
|
||||
beforeEach((): void => {
|
||||
http = new Http(TEST_HTTP_URL);
|
||||
http = new HttpProvider(TEST_HTTP_URL);
|
||||
});
|
||||
|
||||
afterEach((): void => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2017-2020 @polkadot/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { default as HttpProvider } from './http';
|
||||
export { default as WsProvider } from './ws';
|
||||
export { HttpProvider } from './http';
|
||||
export { WsProvider } from './ws';
|
||||
|
||||
@@ -10,10 +10,10 @@ import { MockStateSubscriptions, MockStateSubscriptionCallback, MockStateDb } fr
|
||||
|
||||
import BN from 'bn.js';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import Metadata, { storageFromMeta } from '@polkadot/metadata';
|
||||
import { Metadata, storageFromMeta } from '@polkadot/metadata';
|
||||
import rpcMetadata from '@polkadot/metadata/static';
|
||||
import jsonrpc from '@polkadot/types/interfaces/jsonrpc';
|
||||
import testKeyring from '@polkadot/keyring/testing';
|
||||
import { createTestKeyring } from '@polkadot/keyring/testing';
|
||||
import rpcHeader from '@polkadot/types/json/Header.004.json';
|
||||
import rpcSignedBlock from '@polkadot/types/json/SignedBlock.004.immortal.json';
|
||||
import { assert, bnToU8a, logger, u8aToHex } from '@polkadot/util';
|
||||
@@ -31,7 +31,7 @@ const SUBSCRIPTIONS: string[] = Array.prototype.concat.apply(
|
||||
)
|
||||
) as string[];
|
||||
|
||||
const keyring = testKeyring({ type: 'ed25519' });
|
||||
const keyring = createTestKeyring({ type: 'ed25519' });
|
||||
const l = logger('api-mock');
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,468 +0,0 @@
|
||||
// Copyright 2017-2020 @polkadot/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback, ProviderInterfaceEmitted, ProviderInterfaceEmitCb } from '../types';
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { assert, isChildClass, isNull, isUndefined, logger } from '@polkadot/util';
|
||||
import WS from '@polkadot/x-ws';
|
||||
|
||||
import Coder from '../coder';
|
||||
import defaults from '../defaults';
|
||||
import { getWSErrorString } from './errors';
|
||||
|
||||
interface SubscriptionHandler {
|
||||
callback: ProviderInterfaceCallback;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface WsStateAwaiting {
|
||||
callback: ProviderInterfaceCallback;
|
||||
method: string;
|
||||
params: any[];
|
||||
subscription?: SubscriptionHandler;
|
||||
}
|
||||
|
||||
interface WsStateSubscription extends SubscriptionHandler {
|
||||
method: string;
|
||||
params: any[];
|
||||
}
|
||||
|
||||
const ALIASSES: { [index: string]: string } = {
|
||||
chain_finalisedHead: 'chain_finalizedHead',
|
||||
chain_subscribeFinalisedHeads: 'chain_subscribeFinalizedHeads',
|
||||
chain_unsubscribeFinalisedHeads: 'chain_unsubscribeFinalizedHeads'
|
||||
};
|
||||
|
||||
const RETRY_DELAY = 1000;
|
||||
|
||||
const l = logger('api-ws');
|
||||
|
||||
/**
|
||||
* # @polkadot/rpc-provider/ws
|
||||
*
|
||||
* @name WsProvider
|
||||
*
|
||||
* @description The WebSocket Provider allows sending requests using WebSocket to a WebSocket RPC server TCP port. Unlike the [[HttpProvider]], it does support subscriptions and allows listening to events such as new blocks or balance changes.
|
||||
*
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import Api from '@polkadot/api/promise';
|
||||
* import WsProvider from '@polkadot/rpc-provider/ws';
|
||||
*
|
||||
* const provider = new WsProvider('ws://127.0.0.1:9944');
|
||||
* const api = new Api(provider);
|
||||
* ```
|
||||
*
|
||||
* @see [[HttpProvider]]
|
||||
*/
|
||||
export default class WsProvider implements ProviderInterface {
|
||||
readonly #coder: Coder;
|
||||
|
||||
readonly #endpoints: string[];
|
||||
|
||||
readonly #headers: Record<string, string>;
|
||||
|
||||
readonly #eventemitter: EventEmitter;
|
||||
|
||||
readonly #handlers: Record<string, WsStateAwaiting> = {};
|
||||
|
||||
readonly #queued: Record<string, string> = {};
|
||||
|
||||
readonly #waitingForId: Record<string, JsonRpcResponse> = {};
|
||||
|
||||
#autoConnectMs: number;
|
||||
|
||||
#endpointIndex: number;
|
||||
|
||||
#isConnected = false;
|
||||
|
||||
#subscriptions: Record<string, WsStateSubscription> = {};
|
||||
|
||||
#websocket: WebSocket | null;
|
||||
|
||||
/**
|
||||
* @param {string | string[]} endpoint The endpoint url. Usually `ws://ip:9944` or `wss://ip:9944`, may provide an array of endpoint strings.
|
||||
* @param {boolean} autoConnect Whether to connect automatically or not.
|
||||
*/
|
||||
constructor (endpoint: string | string[] = defaults.WS_URL, autoConnectMs: number | false = RETRY_DELAY, headers: Record<string, string> = {}) {
|
||||
const endpoints = Array.isArray(endpoint)
|
||||
? endpoint
|
||||
: [endpoint];
|
||||
|
||||
assert(endpoints.length !== 0, 'WsProvider requires at least one Endpoint');
|
||||
|
||||
endpoints.forEach((endpoint) => {
|
||||
assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
|
||||
});
|
||||
|
||||
this.#eventemitter = new EventEmitter();
|
||||
this.#autoConnectMs = autoConnectMs || 0;
|
||||
this.#coder = new Coder();
|
||||
this.#endpointIndex = -1;
|
||||
this.#endpoints = endpoints;
|
||||
this.#headers = headers;
|
||||
this.#websocket = null;
|
||||
|
||||
if (autoConnectMs > 0) {
|
||||
this.connectWithRetry().catch((): void => {
|
||||
// does not throw
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary `true` when this provider supports subscriptions
|
||||
*/
|
||||
public get hasSubscriptions (): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Whether the node is connected or not.
|
||||
* @return {boolean} true if connected
|
||||
*/
|
||||
public get isConnected (): boolean {
|
||||
return this.#isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of the object
|
||||
*/
|
||||
public clone (): WsProvider {
|
||||
return new WsProvider(this.#endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Manually connect
|
||||
* @description The [[WsProvider]] connects automatically by default, however if you decided otherwise, you may
|
||||
* connect manually using this method.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async connect (): Promise<void> {
|
||||
try {
|
||||
this.#endpointIndex = (this.#endpointIndex + 1) % this.#endpoints.length;
|
||||
this.#websocket = typeof WebSocket !== 'undefined' && isChildClass(WebSocket, WS)
|
||||
? new WS(this.#endpoints[this.#endpointIndex])
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - WS may be an instance of w3cwebsocket, which supports headers
|
||||
: new WS(this.#endpoints[this.#endpointIndex], undefined, undefined, this.#headers, undefined, {
|
||||
// default: true
|
||||
fragmentOutgoingMessages: true,
|
||||
// default: 16K
|
||||
fragmentationThreshold: 256 * 1024
|
||||
});
|
||||
|
||||
this.#websocket.onclose = this.#onSocketClose;
|
||||
this.#websocket.onerror = this.#onSocketError;
|
||||
this.#websocket.onmessage = this.#onSocketMessage;
|
||||
this.#websocket.onopen = this.#onSocketOpen;
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
|
||||
this.#emit('error', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Connect, never throwing an error, but rather forcing a retry
|
||||
*/
|
||||
public async connectWithRetry (): Promise<void> {
|
||||
try {
|
||||
await this.connect();
|
||||
} catch (error) {
|
||||
setTimeout((): void => {
|
||||
this.connectWithRetry().catch((): void => {
|
||||
// does not throw
|
||||
});
|
||||
}, this.#autoConnectMs || RETRY_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually disconnect from the connection, clearing autoconnect logic
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async disconnect (): Promise<void> {
|
||||
try {
|
||||
assert(!isNull(this.#websocket), 'Cannot disconnect on a non-connected websocket');
|
||||
|
||||
// switch off autoConnect, we are in manual mode now
|
||||
this.#autoConnectMs = 0;
|
||||
|
||||
// 1000 - Normal closure; the connection successfully completed
|
||||
this.#websocket.close(1000);
|
||||
this.#websocket = null;
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
|
||||
this.#emit('error', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Listens on events after having subscribed using the [[subscribe]] function.
|
||||
* @param {ProviderInterfaceEmitted} type Event
|
||||
* @param {ProviderInterfaceEmitCb} sub Callback
|
||||
* @return unsubscribe function
|
||||
*/
|
||||
public on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void {
|
||||
this.#eventemitter.on(type, sub);
|
||||
|
||||
return (): void => {
|
||||
this.#eventemitter.removeListener(type, sub);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Send JSON data using WebSockets to configured HTTP Endpoint or queue.
|
||||
* @param method The RPC methods to execute
|
||||
* @param params Encoded paramaters as appliucable for the method
|
||||
* @param subscription Subscription details (internally used)
|
||||
*/
|
||||
public send (method: string, params: any[], subscription?: SubscriptionHandler): Promise<any> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
try {
|
||||
const json = this.#coder.encodeJson(method, params);
|
||||
const id = this.#coder.getId();
|
||||
|
||||
const callback = (error?: Error | null, result?: any): void => {
|
||||
error
|
||||
? reject(error)
|
||||
: resolve(result);
|
||||
};
|
||||
|
||||
l.debug((): string[] => ['calling', method, json]);
|
||||
|
||||
this.#handlers[id] = {
|
||||
callback,
|
||||
method,
|
||||
params,
|
||||
subscription
|
||||
};
|
||||
|
||||
if (this.isConnected && !isNull(this.#websocket)) {
|
||||
this.#websocket.send(json);
|
||||
} else {
|
||||
this.#queued[id] = json;
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name subscribe
|
||||
* @summary Allows subscribing to a specific event.
|
||||
* @param {string} type Subscription type
|
||||
* @param {string} method Subscription method
|
||||
* @param {any[]} params Parameters
|
||||
* @param {ProviderInterfaceCallback} callback Callback
|
||||
* @return {Promise<number>} Promise resolving to the dd of the subscription you can use with [[unsubscribe]].
|
||||
*
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* const provider = new WsProvider('ws://127.0.0.1:9944');
|
||||
* const rpc = new Rpc(provider);
|
||||
*
|
||||
* rpc.state.subscribeStorage([[storage.system.account, <Address>]], (_, values) => {
|
||||
* console.log(values)
|
||||
* }).then((subscriptionId) => {
|
||||
* console.log('balance changes subscription id: ', subscriptionId)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
public async subscribe (type: string, method: string, params: any[], callback: ProviderInterfaceCallback): Promise<number | string> {
|
||||
const id = await this.send(method, params, { callback, type }) as Promise<number | string>;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Allows unsubscribing to subscriptions made with [[subscribe]].
|
||||
*/
|
||||
public async unsubscribe (type: string, method: string, id: number | string): Promise<boolean> {
|
||||
const subscription = `${type}::${id}`;
|
||||
|
||||
// FIXME This now could happen with re-subscriptions. The issue is that with a re-sub
|
||||
// the assigned id now does not match what the API user originally received. It has
|
||||
// a slight complication in solving - since we cannot rely on the send id, but rather
|
||||
// need to find the actual subscription id to map it
|
||||
if (isUndefined(this.#subscriptions[subscription])) {
|
||||
l.debug((): string => `Unable to find active subscription=${subscription}`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
delete this.#subscriptions[subscription];
|
||||
|
||||
const result = await this.send(method, [id]) as Promise<boolean>;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#emit = (type: ProviderInterfaceEmitted, ...args: any[]): void => {
|
||||
this.#eventemitter.emit(type, ...args);
|
||||
}
|
||||
|
||||
#onSocketClose = (event: CloseEvent): void => {
|
||||
if (this.#autoConnectMs > 0) {
|
||||
l.error(`disconnected from ${this.#endpoints[this.#endpointIndex]}: ${event.code}:: ${event.reason || getWSErrorString(event.code)}`);
|
||||
}
|
||||
|
||||
this.#isConnected = false;
|
||||
this.#emit('disconnected');
|
||||
|
||||
if (this.#autoConnectMs > 0) {
|
||||
setTimeout((): void => {
|
||||
this.connectWithRetry().catch(() => {
|
||||
// does not throw
|
||||
});
|
||||
}, this.#autoConnectMs);
|
||||
}
|
||||
}
|
||||
|
||||
#onSocketError = (error: Event): void => {
|
||||
l.debug((): any => ['socket error', error]);
|
||||
this.#emit('error', error);
|
||||
}
|
||||
|
||||
#onSocketMessage = (message: MessageEvent): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
l.debug(() => ['received', message.data]);
|
||||
|
||||
const response = JSON.parse(message.data as string) as JsonRpcResponse;
|
||||
|
||||
return isUndefined(response.method)
|
||||
? this.#onSocketMessageResult(response)
|
||||
: this.#onSocketMessageSubscribe(response);
|
||||
}
|
||||
|
||||
#onSocketMessageResult = (response: JsonRpcResponse): void => {
|
||||
const handler = this.#handlers[response.id];
|
||||
|
||||
if (!handler) {
|
||||
l.debug((): string => `Unable to find handler for id=${response.id}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { method, params, subscription } = handler;
|
||||
const result = this.#coder.decodeResponse(response) as string;
|
||||
|
||||
// first send the result - in case of subs, we may have an update
|
||||
// immediately if we have some queued results already
|
||||
handler.callback(null, result);
|
||||
|
||||
if (subscription) {
|
||||
const subId = `${subscription.type}::${result}`;
|
||||
|
||||
this.#subscriptions[subId] = {
|
||||
...subscription,
|
||||
method,
|
||||
params
|
||||
};
|
||||
|
||||
// if we have a result waiting for this subscription already
|
||||
if (this.#waitingForId[subId]) {
|
||||
this.#onSocketMessageSubscribe(this.#waitingForId[subId]);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
handler.callback(error, undefined);
|
||||
}
|
||||
|
||||
delete this.#handlers[response.id];
|
||||
}
|
||||
|
||||
#onSocketMessageSubscribe = (response: JsonRpcResponse): void => {
|
||||
const method = ALIASSES[response.method as string] || response.method || 'invalid';
|
||||
const subId = `${method}::${response.params.subscription}`;
|
||||
const handler = this.#subscriptions[subId];
|
||||
|
||||
if (!handler) {
|
||||
// store the JSON, we could have out-of-order subid coming in
|
||||
this.#waitingForId[subId] = response;
|
||||
|
||||
l.debug((): string => `Unable to find handler for subscription=${subId}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// housekeeping
|
||||
delete this.#waitingForId[subId];
|
||||
|
||||
try {
|
||||
const result = this.#coder.decodeResponse(response);
|
||||
|
||||
handler.callback(null, result);
|
||||
} catch (error) {
|
||||
handler.callback(error, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
#onSocketOpen = (): boolean => {
|
||||
assert(!isNull(this.#websocket), 'WebSocket cannot be null in onOpen');
|
||||
|
||||
l.debug((): any[] => ['connected to', this.#endpoints[this.#endpointIndex]]);
|
||||
|
||||
this.#isConnected = true;
|
||||
|
||||
this.#emit('connected');
|
||||
this.#sendQueue();
|
||||
this.#resubscribe();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#resubscribe = (): void => {
|
||||
const subscriptions = this.#subscriptions;
|
||||
|
||||
this.#subscriptions = {};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
Object.keys(subscriptions).forEach(async (id): Promise<void> => {
|
||||
const { callback, method, params, type } = subscriptions[id];
|
||||
|
||||
// only re-create subscriptions which are not in author (only area where
|
||||
// transactions are created, i.e. submissions such as 'author_submitAndWatchExtrinsic'
|
||||
// are not included (and will not be re-broadcast)
|
||||
if (type.startsWith('author_')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.subscribe(type, method, params, callback);
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#sendQueue = (): void => {
|
||||
Object.keys(this.#queued).forEach((id): void => {
|
||||
try {
|
||||
// we have done the websocket check in onSocketOpen, if an issue, will catch it
|
||||
(this.#websocket as WebSocket).send(
|
||||
this.#queued[id]
|
||||
);
|
||||
|
||||
delete this.#queued[id];
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import WsProvider from './';
|
||||
import { WsProvider } from './';
|
||||
|
||||
function sleepMs (ms = 0): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import WsProvider from './';
|
||||
import { WsProvider } from './';
|
||||
|
||||
let ws: WsProvider;
|
||||
let mock: Mock;
|
||||
|
||||
@@ -1,6 +1,468 @@
|
||||
// Copyright 2017-2020 @polkadot/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import WsProvider from './Provider';
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
export default WsProvider;
|
||||
import { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback, ProviderInterfaceEmitted, ProviderInterfaceEmitCb } from '../types';
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { assert, isChildClass, isNull, isUndefined, logger } from '@polkadot/util';
|
||||
import WS from '@polkadot/x-ws';
|
||||
|
||||
import Coder from '../coder';
|
||||
import defaults from '../defaults';
|
||||
import { getWSErrorString } from './errors';
|
||||
|
||||
interface SubscriptionHandler {
|
||||
callback: ProviderInterfaceCallback;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface WsStateAwaiting {
|
||||
callback: ProviderInterfaceCallback;
|
||||
method: string;
|
||||
params: any[];
|
||||
subscription?: SubscriptionHandler;
|
||||
}
|
||||
|
||||
interface WsStateSubscription extends SubscriptionHandler {
|
||||
method: string;
|
||||
params: any[];
|
||||
}
|
||||
|
||||
const ALIASSES: { [index: string]: string } = {
|
||||
chain_finalisedHead: 'chain_finalizedHead',
|
||||
chain_subscribeFinalisedHeads: 'chain_subscribeFinalizedHeads',
|
||||
chain_unsubscribeFinalisedHeads: 'chain_unsubscribeFinalizedHeads'
|
||||
};
|
||||
|
||||
const RETRY_DELAY = 1000;
|
||||
|
||||
const l = logger('api-ws');
|
||||
|
||||
/**
|
||||
* # @polkadot/rpc-provider/ws
|
||||
*
|
||||
* @name WsProvider
|
||||
*
|
||||
* @description The WebSocket Provider allows sending requests using WebSocket to a WebSocket RPC server TCP port. Unlike the [[HttpProvider]], it does support subscriptions and allows listening to events such as new blocks or balance changes.
|
||||
*
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* import Api from '@polkadot/api/promise';
|
||||
* import { WsProvider } from '@polkadot/rpc-provider/ws';
|
||||
*
|
||||
* const provider = new WsProvider('ws://127.0.0.1:9944');
|
||||
* const api = new Api(provider);
|
||||
* ```
|
||||
*
|
||||
* @see [[HttpProvider]]
|
||||
*/
|
||||
export class WsProvider implements ProviderInterface {
|
||||
readonly #coder: Coder;
|
||||
|
||||
readonly #endpoints: string[];
|
||||
|
||||
readonly #headers: Record<string, string>;
|
||||
|
||||
readonly #eventemitter: EventEmitter;
|
||||
|
||||
readonly #handlers: Record<string, WsStateAwaiting> = {};
|
||||
|
||||
readonly #queued: Record<string, string> = {};
|
||||
|
||||
readonly #waitingForId: Record<string, JsonRpcResponse> = {};
|
||||
|
||||
#autoConnectMs: number;
|
||||
|
||||
#endpointIndex: number;
|
||||
|
||||
#isConnected = false;
|
||||
|
||||
#subscriptions: Record<string, WsStateSubscription> = {};
|
||||
|
||||
#websocket: WebSocket | null;
|
||||
|
||||
/**
|
||||
* @param {string | string[]} endpoint The endpoint url. Usually `ws://ip:9944` or `wss://ip:9944`, may provide an array of endpoint strings.
|
||||
* @param {boolean} autoConnect Whether to connect automatically or not.
|
||||
*/
|
||||
constructor (endpoint: string | string[] = defaults.WS_URL, autoConnectMs: number | false = RETRY_DELAY, headers: Record<string, string> = {}) {
|
||||
const endpoints = Array.isArray(endpoint)
|
||||
? endpoint
|
||||
: [endpoint];
|
||||
|
||||
assert(endpoints.length !== 0, 'WsProvider requires at least one Endpoint');
|
||||
|
||||
endpoints.forEach((endpoint) => {
|
||||
assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
|
||||
});
|
||||
|
||||
this.#eventemitter = new EventEmitter();
|
||||
this.#autoConnectMs = autoConnectMs || 0;
|
||||
this.#coder = new Coder();
|
||||
this.#endpointIndex = -1;
|
||||
this.#endpoints = endpoints;
|
||||
this.#headers = headers;
|
||||
this.#websocket = null;
|
||||
|
||||
if (autoConnectMs > 0) {
|
||||
this.connectWithRetry().catch((): void => {
|
||||
// does not throw
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary `true` when this provider supports subscriptions
|
||||
*/
|
||||
public get hasSubscriptions (): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Whether the node is connected or not.
|
||||
* @return {boolean} true if connected
|
||||
*/
|
||||
public get isConnected (): boolean {
|
||||
return this.#isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of the object
|
||||
*/
|
||||
public clone (): WsProvider {
|
||||
return new WsProvider(this.#endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Manually connect
|
||||
* @description The [[WsProvider]] connects automatically by default, however if you decided otherwise, you may
|
||||
* connect manually using this method.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async connect (): Promise<void> {
|
||||
try {
|
||||
this.#endpointIndex = (this.#endpointIndex + 1) % this.#endpoints.length;
|
||||
this.#websocket = typeof WebSocket !== 'undefined' && isChildClass(WebSocket, WS)
|
||||
? new WS(this.#endpoints[this.#endpointIndex])
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - WS may be an instance of w3cwebsocket, which supports headers
|
||||
: new WS(this.#endpoints[this.#endpointIndex], undefined, undefined, this.#headers, undefined, {
|
||||
// default: true
|
||||
fragmentOutgoingMessages: true,
|
||||
// default: 16K
|
||||
fragmentationThreshold: 256 * 1024
|
||||
});
|
||||
|
||||
this.#websocket.onclose = this.#onSocketClose;
|
||||
this.#websocket.onerror = this.#onSocketError;
|
||||
this.#websocket.onmessage = this.#onSocketMessage;
|
||||
this.#websocket.onopen = this.#onSocketOpen;
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
|
||||
this.#emit('error', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Connect, never throwing an error, but rather forcing a retry
|
||||
*/
|
||||
public async connectWithRetry (): Promise<void> {
|
||||
try {
|
||||
await this.connect();
|
||||
} catch (error) {
|
||||
setTimeout((): void => {
|
||||
this.connectWithRetry().catch((): void => {
|
||||
// does not throw
|
||||
});
|
||||
}, this.#autoConnectMs || RETRY_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually disconnect from the connection, clearing autoconnect logic
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async disconnect (): Promise<void> {
|
||||
try {
|
||||
assert(!isNull(this.#websocket), 'Cannot disconnect on a non-connected websocket');
|
||||
|
||||
// switch off autoConnect, we are in manual mode now
|
||||
this.#autoConnectMs = 0;
|
||||
|
||||
// 1000 - Normal closure; the connection successfully completed
|
||||
this.#websocket.close(1000);
|
||||
this.#websocket = null;
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
|
||||
this.#emit('error', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Listens on events after having subscribed using the [[subscribe]] function.
|
||||
* @param {ProviderInterfaceEmitted} type Event
|
||||
* @param {ProviderInterfaceEmitCb} sub Callback
|
||||
* @return unsubscribe function
|
||||
*/
|
||||
public on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void {
|
||||
this.#eventemitter.on(type, sub);
|
||||
|
||||
return (): void => {
|
||||
this.#eventemitter.removeListener(type, sub);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Send JSON data using WebSockets to configured HTTP Endpoint or queue.
|
||||
* @param method The RPC methods to execute
|
||||
* @param params Encoded paramaters as appliucable for the method
|
||||
* @param subscription Subscription details (internally used)
|
||||
*/
|
||||
public send (method: string, params: any[], subscription?: SubscriptionHandler): Promise<any> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
try {
|
||||
const json = this.#coder.encodeJson(method, params);
|
||||
const id = this.#coder.getId();
|
||||
|
||||
const callback = (error?: Error | null, result?: any): void => {
|
||||
error
|
||||
? reject(error)
|
||||
: resolve(result);
|
||||
};
|
||||
|
||||
l.debug((): string[] => ['calling', method, json]);
|
||||
|
||||
this.#handlers[id] = {
|
||||
callback,
|
||||
method,
|
||||
params,
|
||||
subscription
|
||||
};
|
||||
|
||||
if (this.isConnected && !isNull(this.#websocket)) {
|
||||
this.#websocket.send(json);
|
||||
} else {
|
||||
this.#queued[id] = json;
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name subscribe
|
||||
* @summary Allows subscribing to a specific event.
|
||||
* @param {string} type Subscription type
|
||||
* @param {string} method Subscription method
|
||||
* @param {any[]} params Parameters
|
||||
* @param {ProviderInterfaceCallback} callback Callback
|
||||
* @return {Promise<number>} Promise resolving to the dd of the subscription you can use with [[unsubscribe]].
|
||||
*
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* const provider = new WsProvider('ws://127.0.0.1:9944');
|
||||
* const rpc = new Rpc(provider);
|
||||
*
|
||||
* rpc.state.subscribeStorage([[storage.system.account, <Address>]], (_, values) => {
|
||||
* console.log(values)
|
||||
* }).then((subscriptionId) => {
|
||||
* console.log('balance changes subscription id: ', subscriptionId)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
public async subscribe (type: string, method: string, params: any[], callback: ProviderInterfaceCallback): Promise<number | string> {
|
||||
const id = await this.send(method, params, { callback, type }) as Promise<number | string>;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Allows unsubscribing to subscriptions made with [[subscribe]].
|
||||
*/
|
||||
public async unsubscribe (type: string, method: string, id: number | string): Promise<boolean> {
|
||||
const subscription = `${type}::${id}`;
|
||||
|
||||
// FIXME This now could happen with re-subscriptions. The issue is that with a re-sub
|
||||
// the assigned id now does not match what the API user originally received. It has
|
||||
// a slight complication in solving - since we cannot rely on the send id, but rather
|
||||
// need to find the actual subscription id to map it
|
||||
if (isUndefined(this.#subscriptions[subscription])) {
|
||||
l.debug((): string => `Unable to find active subscription=${subscription}`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
delete this.#subscriptions[subscription];
|
||||
|
||||
const result = await this.send(method, [id]) as Promise<boolean>;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#emit = (type: ProviderInterfaceEmitted, ...args: any[]): void => {
|
||||
this.#eventemitter.emit(type, ...args);
|
||||
}
|
||||
|
||||
#onSocketClose = (event: CloseEvent): void => {
|
||||
if (this.#autoConnectMs > 0) {
|
||||
l.error(`disconnected from ${this.#endpoints[this.#endpointIndex]}: ${event.code}:: ${event.reason || getWSErrorString(event.code)}`);
|
||||
}
|
||||
|
||||
this.#isConnected = false;
|
||||
this.#emit('disconnected');
|
||||
|
||||
if (this.#autoConnectMs > 0) {
|
||||
setTimeout((): void => {
|
||||
this.connectWithRetry().catch(() => {
|
||||
// does not throw
|
||||
});
|
||||
}, this.#autoConnectMs);
|
||||
}
|
||||
}
|
||||
|
||||
#onSocketError = (error: Event): void => {
|
||||
l.debug((): any => ['socket error', error]);
|
||||
this.#emit('error', error);
|
||||
}
|
||||
|
||||
#onSocketMessage = (message: MessageEvent): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
l.debug(() => ['received', message.data]);
|
||||
|
||||
const response = JSON.parse(message.data as string) as JsonRpcResponse;
|
||||
|
||||
return isUndefined(response.method)
|
||||
? this.#onSocketMessageResult(response)
|
||||
: this.#onSocketMessageSubscribe(response);
|
||||
}
|
||||
|
||||
#onSocketMessageResult = (response: JsonRpcResponse): void => {
|
||||
const handler = this.#handlers[response.id];
|
||||
|
||||
if (!handler) {
|
||||
l.debug((): string => `Unable to find handler for id=${response.id}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { method, params, subscription } = handler;
|
||||
const result = this.#coder.decodeResponse(response) as string;
|
||||
|
||||
// first send the result - in case of subs, we may have an update
|
||||
// immediately if we have some queued results already
|
||||
handler.callback(null, result);
|
||||
|
||||
if (subscription) {
|
||||
const subId = `${subscription.type}::${result}`;
|
||||
|
||||
this.#subscriptions[subId] = {
|
||||
...subscription,
|
||||
method,
|
||||
params
|
||||
};
|
||||
|
||||
// if we have a result waiting for this subscription already
|
||||
if (this.#waitingForId[subId]) {
|
||||
this.#onSocketMessageSubscribe(this.#waitingForId[subId]);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
handler.callback(error, undefined);
|
||||
}
|
||||
|
||||
delete this.#handlers[response.id];
|
||||
}
|
||||
|
||||
#onSocketMessageSubscribe = (response: JsonRpcResponse): void => {
|
||||
const method = ALIASSES[response.method as string] || response.method || 'invalid';
|
||||
const subId = `${method}::${response.params.subscription}`;
|
||||
const handler = this.#subscriptions[subId];
|
||||
|
||||
if (!handler) {
|
||||
// store the JSON, we could have out-of-order subid coming in
|
||||
this.#waitingForId[subId] = response;
|
||||
|
||||
l.debug((): string => `Unable to find handler for subscription=${subId}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// housekeeping
|
||||
delete this.#waitingForId[subId];
|
||||
|
||||
try {
|
||||
const result = this.#coder.decodeResponse(response);
|
||||
|
||||
handler.callback(null, result);
|
||||
} catch (error) {
|
||||
handler.callback(error, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
#onSocketOpen = (): boolean => {
|
||||
assert(!isNull(this.#websocket), 'WebSocket cannot be null in onOpen');
|
||||
|
||||
l.debug((): any[] => ['connected to', this.#endpoints[this.#endpointIndex]]);
|
||||
|
||||
this.#isConnected = true;
|
||||
|
||||
this.#emit('connected');
|
||||
this.#sendQueue();
|
||||
this.#resubscribe();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#resubscribe = (): void => {
|
||||
const subscriptions = this.#subscriptions;
|
||||
|
||||
this.#subscriptions = {};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
Object.keys(subscriptions).forEach(async (id): Promise<void> => {
|
||||
const { callback, method, params, type } = subscriptions[id];
|
||||
|
||||
// only re-create subscriptions which are not in author (only area where
|
||||
// transactions are created, i.e. submissions such as 'author_submitAndWatchExtrinsic'
|
||||
// are not included (and will not be re-broadcast)
|
||||
if (type.startsWith('author_')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.subscribe(type, method, params, callback);
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#sendQueue = (): void => {
|
||||
Object.keys(this.#queued).forEach((id): void => {
|
||||
try {
|
||||
// we have done the websocket check in onSocketOpen, if an issue, will catch it
|
||||
(this.#websocket as WebSocket).send(
|
||||
this.#queued[id]
|
||||
);
|
||||
|
||||
delete this.#queued[id];
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import WsProvider from './';
|
||||
import { WsProvider } from './';
|
||||
|
||||
let ws: WsProvider;
|
||||
let mock: Mock;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Constructor } from '@polkadot/types/types';
|
||||
import { Global, Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import WsProvider from './';
|
||||
import { WsProvider } from './';
|
||||
|
||||
declare const global: Global;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2020 @polkadot/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import WsProvider from './';
|
||||
import { WsProvider } from './';
|
||||
|
||||
describe('state', (): void => {
|
||||
it('requires an ws:// prefixed endpoint', (): void => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Constructor } from '@polkadot/types/types';
|
||||
import { Global, Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import WsProvider from './';
|
||||
import { WsProvider } from './';
|
||||
|
||||
declare const global: Global;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Constructor } from '@polkadot/types/types';
|
||||
import { Global, Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import WsProvider from './';
|
||||
import { WsProvider } from './';
|
||||
|
||||
declare const global: Global;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"@polkadot/metadata": "2.6.2-0",
|
||||
"@polkadot/rpc-provider": "2.6.2-0",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"handlebars": "^4.7.6",
|
||||
"websocket": "^1.0.32",
|
||||
"yargs": "^16.1.0"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import staticData from '@polkadot/metadata/static';
|
||||
import Metadata from '@polkadot/metadata';
|
||||
import { Metadata } from '@polkadot/metadata';
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
import * as defaultDefs from '@polkadot/types/interfaces/definitions';
|
||||
import { stringCamelCase } from '@polkadot/util';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { StorageEntryMetadataLatest } from '@polkadot/types/interfaces/metadata'
|
||||
import { Registry } from '@polkadot/types/types';
|
||||
|
||||
import staticData from '@polkadot/metadata/static';
|
||||
import Metadata from '@polkadot/metadata';
|
||||
import { Metadata } from '@polkadot/metadata';
|
||||
import * as defaultDefs from '@polkadot/types/interfaces/definitions';
|
||||
import { unwrapStorageType } from '@polkadot/types/primitive/StorageKey';
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
|
||||
@@ -6,7 +6,7 @@ import Handlebars from 'handlebars';
|
||||
import { Registry } from '@polkadot/types/types';
|
||||
|
||||
import staticData from '@polkadot/metadata/static';
|
||||
import Metadata from '@polkadot/metadata';
|
||||
import { Metadata } from '@polkadot/metadata';
|
||||
import * as defaultDefs from '@polkadot/types/interfaces/definitions';
|
||||
import { Text } from '@polkadot/types/primitive';
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { MetadataLatest } from '@polkadot/types/interfaces/metadata';
|
||||
import { Codec, DefinitionRpcParam } from '@polkadot/types/types';
|
||||
|
||||
import fs from 'fs';
|
||||
import Metadata from '@polkadot/metadata';
|
||||
import { Metadata } from '@polkadot/metadata';
|
||||
import rpcdata from '@polkadot/metadata/static';
|
||||
import Call from '@polkadot/types/generic/Call';
|
||||
import { unwrapStorageType } from '@polkadot/types/primitive/StorageKey';
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@polkadot/types": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"bn.js": "^5.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -30,15 +30,15 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@polkadot/metadata": "2.6.2-0",
|
||||
"@polkadot/util": "^4.0.1",
|
||||
"@polkadot/util-crypto": "^4.0.1",
|
||||
"@polkadot/util": "^4.0.2-5",
|
||||
"@polkadot/util-crypto": "^4.0.2-5",
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"bn.js": "^5.1.3",
|
||||
"memoizee": "^0.4.14",
|
||||
"rxjs": "^6.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^4.0.1",
|
||||
"@polkadot/keyring": "^4.0.2-5",
|
||||
"@types/memoizee": "^0.4.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import BN from 'bn.js';
|
||||
import { compactAddLength, compactFromU8a, compactStripLength, compactToU8a, isBigInt, isBn, isNumber, isString } from '@polkadot/util';
|
||||
import { DEFAULT_BITLENGTH } from '@polkadot/util/compact/defaults';
|
||||
|
||||
import typeToConstructor from './utils/typeToConstructor';
|
||||
import { typeToConstructor } from './utils';
|
||||
import { UIntBitLength } from './AbstractInt';
|
||||
|
||||
export interface CompactEncodable extends Codec {
|
||||
|
||||
@@ -7,9 +7,7 @@ import { AnyJson, Constructor, Codec, InterfaceTypes, Registry } from '../types'
|
||||
import { isHex, hexToU8a, isObject, isU8a, logger, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import Compact from './Compact';
|
||||
import compareMap from './utils/compareMap';
|
||||
import decodeU8a from './utils/decodeU8a';
|
||||
import typeToConstructor from './utils/typeToConstructor';
|
||||
import { compareMap, decodeU8a, typeToConstructor } from './utils';
|
||||
|
||||
const l = logger('Map');
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { VoteThreshold } from '../interfaces/elections';
|
||||
import { BlockNumber } from '../interfaces/runtime';
|
||||
import { CodecTo } from '../types';
|
||||
|
||||
import Metadata from '@polkadot/metadata';
|
||||
import { Metadata } from '@polkadot/metadata';
|
||||
import rpcMetadata from '@polkadot/metadata/static';
|
||||
|
||||
import { TypeRegistry } from '../create';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { PropIndex } from '../interfaces/democracy';
|
||||
import { Codec, CodecTo } from '../types';
|
||||
|
||||
import Metadata from '@polkadot/metadata';
|
||||
import { Metadata } from '@polkadot/metadata';
|
||||
import rpcMetadata from '@polkadot/metadata/static';
|
||||
|
||||
import { createTypeUnsafe, TypeRegistry } from '../create';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { TypeRegistry } from '../../create';
|
||||
import U32 from '../../primitive/U32';
|
||||
import compareArray from './compareArray';
|
||||
import { compareArray } from '.';
|
||||
|
||||
describe('compareArray', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
@@ -7,7 +7,7 @@ import { hasEq } from './util';
|
||||
|
||||
// NOTE These are used internally and when comparing objects, expects that
|
||||
// when the second is an Codec[] that the first has to be as well
|
||||
export default function compareArray (a: unknown[], b?: unknown): boolean {
|
||||
export function compareArray (a: unknown[], b?: unknown): boolean {
|
||||
if (Array.isArray(b)) {
|
||||
return (a.length === b.length) && isUndefined(
|
||||
a.find((value, index): boolean =>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Codec } from '../../types';
|
||||
import { TypeRegistry } from '../../create';
|
||||
import U32 from '../../primitive/U32';
|
||||
import compareMap from './compareMap';
|
||||
import { compareMap } from '.';
|
||||
|
||||
describe('compareMap', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
@@ -26,7 +26,7 @@ function compareMapArray (a: Map<any, any>, b: [any, any][]): boolean {
|
||||
|
||||
// NOTE These are used internally and when comparing objects, expects that
|
||||
// when the second is an Map<string, Codec> that the first has to be as well
|
||||
export default function compareMap (a: Map<any, any>, b?: unknown): boolean {
|
||||
export function compareMap (a: Map<any, any>, b?: unknown): boolean {
|
||||
if (Array.isArray(b)) {
|
||||
return compareMapArray(a, b);
|
||||
} else if (b instanceof Map) {
|
||||
|
||||
@@ -10,7 +10,7 @@ function compareSetArray (a: Set<unknown>, b: any[]): boolean {
|
||||
|
||||
// NOTE These are used internally and when comparing objects, expects that
|
||||
// when the second is an Set<string, Codec> that the first has to be as well
|
||||
export default function compareSet (a: Set<unknown>, b?: unknown): boolean {
|
||||
export function compareSet (a: Set<unknown>, b?: unknown): boolean {
|
||||
if (Array.isArray(b)) {
|
||||
return compareSetArray(a, b);
|
||||
} else if (b instanceof Set) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Codec, Constructor, Registry } from '../../types';
|
||||
* @param u8a - The u8a to decode.
|
||||
* @param types - The array of Constructor to decode the U8a against.
|
||||
*/
|
||||
export default function decodeU8a (registry: Registry, u8a: Uint8Array, _types: Constructor[] | { [index: string]: Constructor }): Codec[] {
|
||||
export function decodeU8a (registry: Registry, u8a: Uint8Array, _types: Constructor[] | { [index: string]: Constructor }): Codec[] {
|
||||
const types = Array.isArray(_types)
|
||||
? _types
|
||||
: Object.values(_types);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2017-2020 @polkadot/types authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { default as compareArray } from './compareArray';
|
||||
export { default as compareMap } from './compareMap';
|
||||
export { default as compareSet } from './compareSet';
|
||||
export { default as decodeU8a } from './decodeU8a';
|
||||
export { default as mapToTypeMap } from './mapToTypeMap';
|
||||
export { default as typeToConstructor } from './typeToConstructor';
|
||||
export { compareArray } from './compareArray';
|
||||
export { compareMap } from './compareMap';
|
||||
export { compareSet } from './compareSet';
|
||||
export { decodeU8a } from './decodeU8a';
|
||||
export { mapToTypeMap } from './mapToTypeMap';
|
||||
export { typeToConstructor } from './typeToConstructor';
|
||||
|
||||
export * from './encodeTypes';
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
import { Constructor, InterfaceTypes, Registry } from '../../types';
|
||||
|
||||
import typeToConstructor from './typeToConstructor';
|
||||
import { typeToConstructor } from './typeToConstructor';
|
||||
|
||||
/**
|
||||
* @description takes an input map of the form `{ [string]: string | Constructor }` and returns a map of `{ [string]: Constructor }`
|
||||
*/
|
||||
export default function mapToTypeMap (registry: Registry, input: Record<string, keyof InterfaceTypes | Constructor>): Record<string, Constructor> {
|
||||
export function mapToTypeMap (registry: Registry, input: Record<string, keyof InterfaceTypes | Constructor>): Record<string, Constructor> {
|
||||
return Object
|
||||
.entries(input)
|
||||
.reduce((output: Record<string, Constructor>, [key, type]): Record<string, Constructor> => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user