Adjust class privates via #, where applicable (#1921)

* Adjust class priates via #, where applicable

* Remove stray log

* Cleanup rpc-provider

* Small adjustments

* No private access in tests

* Submittable privates

* Use derive for nonce derivation

* Tweak observers

* Alphabetical privates
This commit is contained in:
Jaco Greeff
2020-02-22 11:29:50 +01:00
committed by GitHub
parent ccf32a6289
commit 3bf4c3fac5
32 changed files with 406 additions and 514 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
"@babel/core": "^7.8.4",
"@babel/register": "^7.8.3",
"@babel/runtime": "^7.8.4",
"@polkadot/dev": "^0.40.22",
"@polkadot/dev": "^0.41.1",
"@polkadot/ts": "^0.2.3",
"copyfiles": "^2.2.0"
}
@@ -17,12 +17,12 @@ const _Header: Constructor<Header> = Struct.with(runtimeTypes.types.Header as an
* A [[Block]] header with an additional `author` field that indicates the block author
*/
export default class HeaderExtended extends _Header {
private _author?: AccountId;
readonly #author?: AccountId;
constructor (registry: Registry, header?: Header, sessionValidators?: AccountId[]) {
super(registry, header);
this._author = this.extractAuthor(sessionValidators);
this.#author = this.extractAuthor(sessionValidators);
}
private extractAuthor (sessionValidators: AccountId[] = []): AccountId | undefined {
@@ -51,7 +51,7 @@ export default class HeaderExtended extends _Header {
* @description Convenience method, returns the author for the block
*/
public get author (): AccountId | undefined {
return this._author;
return this.#author;
}
/**
@@ -18,12 +18,12 @@ const _ReferendumInfo: Constructor<ReferendumInfo> = Struct.with(democracyTypes.
* A [[ReferendumInfo]] with an additional `index` field
*/
export default class ReferendumInfoExtended extends _ReferendumInfo {
private _index: ReferendumIndex;
readonly #index: ReferendumIndex;
constructor (registry: Registry, value: ReferendumInfo | ReferendumInfoExtended, index?: BN | number) {
super(registry, value);
this._index = value instanceof ReferendumInfoExtended
this.#index = value instanceof ReferendumInfoExtended
? value.index
: createType(registry, 'ReferendumIndex', index);
}
@@ -32,7 +32,7 @@ export default class ReferendumInfoExtended extends _ReferendumInfo {
* @description Convenience getter, returns the referendumIndex
*/
public get index (): ReferendumIndex {
return this._index;
return this.#index;
}
/**
+5 -5
View File
@@ -7,10 +7,10 @@ import { ApiInterfaceEvents } from '../types';
import EventEmitter from 'eventemitter3';
export default class Events {
private _eventemitter = new EventEmitter();
#eventemitter = new EventEmitter();
protected emit (type: ApiInterfaceEvents, ...args: any[]): boolean {
return this._eventemitter.emit(type, ...args);
return this.#eventemitter.emit(type, ...args);
}
/**
@@ -33,7 +33,7 @@ export default class Events {
* ```
*/
public on (type: ApiInterfaceEvents, handler: (...args: any[]) => any): this {
this._eventemitter.on(type, handler);
this.#eventemitter.on(type, handler);
return this;
}
@@ -60,7 +60,7 @@ export default class Events {
* ```
*/
public off (type: ApiInterfaceEvents, handler: (...args: any[]) => any): this {
this._eventemitter.removeListener(type, handler);
this.#eventemitter.removeListener(type, handler);
return this;
}
@@ -85,7 +85,7 @@ export default class Events {
* ```
*/
public once (type: ApiInterfaceEvents, handler: (...args: any[]) => any): this {
this._eventemitter.once(type, handler);
this.#eventemitter.once(type, handler);
return this;
}
+17 -17
View File
@@ -20,9 +20,9 @@ const KEEPALIVE_INTERVAL = 15000;
const l = logger('api/init');
export default abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
private _healthTimer: NodeJS.Timeout | null = null;
#healthTimer: NodeJS.Timeout | null = null;
private _updateSub?: Subscription;
#updateSub?: Subscription;
constructor (options: ApiOptions, type: ApiTypes, decorateMethod: DecorateMethod<ApiType>) {
super(options, type, decorateMethod);
@@ -44,15 +44,15 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
this._rx.queryMulti = this.decorateMulti(this.rxDecorateMethod);
this._rx.signer = options.signer;
this._rpcCore.provider.on('disconnected', this._onProviderDisconnect);
this._rpcCore.provider.on('error', this._onProviderError);
this._rpcCore.provider.on('connected', this._onProviderConnect);
this._rpcCore.provider.on('disconnected', this.#onProviderDisconnect);
this._rpcCore.provider.on('error', this.#onProviderError);
this._rpcCore.provider.on('connected', this.#onProviderConnect);
// If the provider was instantiated earlier, and has already emitted a
// 'connected' event, then the `on('connected')` won't fire anymore. To
// cater for this case, we call manually `this._onProviderConnect`.
if (this._rpcCore.provider.isConnected()) {
this._onProviderConnect();
this.#onProviderConnect();
}
}
@@ -70,8 +70,8 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
this._genesisHash = genesisHash;
if (this._updateSub) {
this._updateSub.unsubscribe();
if (this.#updateSub) {
this.#updateSub.unsubscribe();
}
const { metadata = {} } = this._options;
@@ -109,11 +109,11 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
// subscribe to metadata updates, inject the types on changes
private subscribeUpdates (): void {
if (this._updateSub) {
if (this.#updateSub) {
return;
}
this._updateSub = this._rpcCore.state.subscribeRuntimeVersion().pipe(
this.#updateSub = this._rpcCore.state.subscribeRuntimeVersion().pipe(
switchMap((version: RuntimeVersion) =>
combineLatest(
of(version),
@@ -201,7 +201,7 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
return true;
}
private _onProviderConnect = async (): Promise<void> => {
#onProviderConnect = async (): Promise<void> => {
this.emit('connected');
this._isConnected.next(true);
@@ -217,7 +217,7 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
this.emit('ready', this);
}
this._healthTimer = setInterval((): void => {
this.#healthTimer = setInterval((): void => {
this._rpcCore.system.health().toPromise().catch((): void => {
// ignore
});
@@ -231,17 +231,17 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
}
}
private _onProviderDisconnect = (): void => {
#onProviderDisconnect = (): void => {
this.emit('disconnected');
this._isConnected.next(false);
if (this._healthTimer) {
clearInterval(this._healthTimer);
this._healthTimer = null;
if (this.#healthTimer) {
clearInterval(this.#healthTimer);
this.#healthTimer = null;
}
};
private _onProviderError = (error: Error): void => {
#onProviderError = (error: Error): void => {
this.emit('error', error);
};
}
+13 -7
View File
@@ -22,15 +22,21 @@ interface SignerRawOptions {
signer?: Signer;
}
let pkgJson: { name: string; version: string };
try {
pkgJson = require('../package.json');
} catch (error) {
// development environment
pkgJson = { name: '@polkadot/api', version: '-' };
interface PkgJson {
name: string;
version: string;
}
let pkgJson: PkgJson = { name: '@polkadot/api', version: '-' };
import('../package.json')
.then((_pkgJson: any): void => {
pkgJson = _pkgJson;
})
.catch((): void => {
// ignore
});
function assertResult<T> (value: T | undefined): T {
assert(!isUndefined(value), 'Api needs to be initialized before using, listen on \'ready\'');
+1
View File
@@ -4,6 +4,7 @@
import { detectPackage } from '@polkadot/util';
// FIXME This really should be `import(...).then(...)`, but need to check rejections
// eslint-disable-next-line @typescript-eslint/no-var-requires
detectPackage(require('./package.json'), __dirname);
+3 -3
View File
@@ -173,7 +173,7 @@ export function decorateMethod<Method extends AnyFunction> (method: Method, opti
* ```
*/
export default class ApiPromise extends ApiBase<'promise'> {
private _isReadyPromise: Promise<ApiPromise>;
#isReadyPromise: Promise<ApiPromise>;
/**
* @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance.
@@ -216,7 +216,7 @@ export default class ApiPromise extends ApiBase<'promise'> {
constructor (options?: ApiOptions) {
super(options, 'promise', decorateMethod);
this._isReadyPromise = new Promise((resolve, reject): void => {
this.#isReadyPromise = new Promise((resolve, reject): void => {
super.once('ready', (): void => {
resolve(this);
});
@@ -230,7 +230,7 @@ export default class ApiPromise extends ApiBase<'promise'> {
* @description Promise that returns the first time we are connected and loaded
*/
public get isReady (): Promise<ApiPromise> {
return this._isReadyPromise;
return this.#isReadyPromise;
}
/**
+21 -21
View File
@@ -13,31 +13,31 @@ export interface CombinatorFunction {
}
export default class Combinator {
protected _allHasFired = false;
#allHasFired = false;
protected _callback: CombinatorCallback;
#callback: CombinatorCallback;
protected _fired: boolean[] = [];
#fired: boolean[] = [];
protected _fns: CombinatorFunction[] = [];
#fns: CombinatorFunction[] = [];
protected _isActive = true;
#isActive = true;
protected _results: any[] = [];
#results: any[] = [];
protected _subscriptions: UnsubscribePromise[] = [];
#subscriptions: UnsubscribePromise[] = [];
constructor (fns: (CombinatorFunction | [CombinatorFunction, ...any[]])[], callback: CombinatorCallback) {
this._callback = callback;
this.#callback = callback;
// eslint-disable-next-line @typescript-eslint/require-await
this._subscriptions = fns.map(async (input, index): UnsubscribePromise => {
this.#subscriptions = fns.map(async (input, index): UnsubscribePromise => {
const [fn, ...args] = Array.isArray(input)
? input
: [input];
this._fired.push(false);
this._fns.push(fn);
this.#fired.push(false);
this.#fns.push(fn);
// Not quite 100% how to have a variable number at the front here
return (fn as Function)(...args, this.createCallback(index));
@@ -45,43 +45,43 @@ export default class Combinator {
}
protected allHasFired (): boolean {
if (!this._allHasFired) {
this._allHasFired = this._fired.filter((hasFired): boolean => !hasFired).length === 0;
if (!this.#allHasFired) {
this.#allHasFired = this.#fired.filter((hasFired): boolean => !hasFired).length === 0;
}
return this._allHasFired;
return this.#allHasFired;
}
protected createCallback (index: number): (value: any) => void {
return (value: any): void => {
this._fired[index] = true;
this._results[index] = value;
this.#fired[index] = true;
this.#results[index] = value;
this.triggerUpdate();
};
}
protected triggerUpdate (): void {
if (!this._isActive || !isFunction(this._callback) || !this.allHasFired()) {
if (!this.#isActive || !isFunction(this.#callback) || !this.allHasFired()) {
return;
}
try {
this._callback(this._results);
this.#callback(this.#results);
} catch (error) {
// swallow, we don't want the handler to trip us up
}
}
public unsubscribe (): void {
if (!this._isActive) {
if (!this.#isActive) {
return;
}
this._isActive = false;
this.#isActive = false;
// eslint-disable-next-line @typescript-eslint/no-misused-promises
this._subscriptions.forEach(async (subscription): Promise<void> => {
this.#subscriptions.forEach(async (subscription): Promise<void> => {
try {
const unsubscribe = await subscription;
+3 -3
View File
@@ -114,7 +114,7 @@ export function decorateMethod <Method extends AnyFunction> (method: Method): Me
* ```
*/
export default class ApiRx extends ApiBase<'rxjs'> {
private _isReadyRx: Observable<ApiRx>;
#isReadyRx: Observable<ApiRx>;
/**
* @description Creates an ApiRx instance using the supplied provider. Returns an Observable containing the actual Api instance.
@@ -163,7 +163,7 @@ export default class ApiRx extends ApiBase<'rxjs'> {
constructor (options?: ApiOptions) {
super(options, 'rxjs', decorateMethod);
this._isReadyRx = from(
this.#isReadyRx = from(
// You can create an observable from an event, however my mind groks this form better
new Promise((resolve): void => {
super.on('ready', (): void => {
@@ -184,7 +184,7 @@ export default class ApiRx extends ApiBase<'rxjs'> {
* @description Observable that returns the first time we are connected and loaded
*/
public get isReady (): Observable<ApiRx> {
return this._isReadyRx;
return this.#isReadyRx;
}
/**
+136 -149
View File
@@ -3,8 +3,8 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountData, Address, Call, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
import { Callback, Codec, Constructor, IKeyringPair, ITuple, Registry, SignatureOptions, ISubmittableResult } from '@polkadot/types/types';
import { Address, Call, ExtrinsicEra, ExtrinsicStatus, Hash, Header, Index, RuntimeDispatchInfo } from '@polkadot/types/interfaces';
import { Callback, Codec, Constructor, IKeyringPair, Registry, SignatureOptions, ISubmittableResult } from '@polkadot/types/types';
import { ApiInterfaceRx, ApiTypes, SignerResult } from '../types';
import { AddressOrPair, SignerOptions, SubmittableExtrinsic, SubmittablePaymentResult, SubmittableResultResult, SubmittableResultSubscription, SubmittableThis } from './types';
@@ -35,38 +35,32 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
const Extrinsic = ClassOf(api.registry, 'Extrinsic');
return class Submittable extends Extrinsic implements SubmittableExtrinsic<ApiType> {
private readonly _api: ApiInterfaceRx;
private readonly _decorateMethod: ApiBase<ApiType>['decorateMethod'];
private readonly _ignoreStatusCb: boolean;
readonly #ignoreStatusCb: boolean;
constructor (registry: Registry, extrinsic: Call | Uint8Array | string) {
super(registry, extrinsic, { version: api.extrinsicType });
this._api = api;
this._decorateMethod = decorateMethod;
this._ignoreStatusCb = apiType === 'rxjs';
this.#ignoreStatusCb = apiType === 'rxjs';
}
// calculate the payment info for this transaction (if signed and submitted)
public paymentInfo (account: AddressOrPair, options?: Partial<SignerOptions>): SubmittablePaymentResult<ApiType> {
const [allOptions] = this._makeSignAndSendOptions(options);
const [allOptions] = this.#makeSignAndSendOptions(options);
const address = isKeyringPair(account) ? account.address : account.toString();
return this._decorateMethod(
return decorateMethod(
(): Observable<RuntimeDispatchInfo> =>
this._getPrelimState(address, allOptions).pipe(
this.#getPrelimState(address, allOptions).pipe(
first(),
switchMap(([nonce, header]): Observable<RuntimeDispatchInfo> => {
// setup our options (same way as in signAndSend)
const eraOptions = this._makeEraOptions(allOptions, { header, nonce });
const signOptions = this._makeSignOptions(eraOptions, {});
const eraOptions = this.#makeEraOptions(allOptions, { header, nonce });
const signOptions = this.#makeSignOptions(eraOptions, {});
// add a fake signature to the extrinsic
this.signFake(address, signOptions);
return this._api.rpc.payment.queryInfo(this.toHex());
return api.rpc.payment.queryInfo(this.toHex());
})
)
)();
@@ -74,7 +68,7 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
// sign a transaction, returning the this to allow chaining, i.e. .sign(...).send()
public sign (account: IKeyringPair, optionsOrNonce: Partial<SignerOptions>): this {
super.sign(account, this._makeSignOptions(this._optionsOrNonce(optionsOrNonce), {}));
super.sign(account, this.#makeSignOptions(this.#optionsOrNonce(optionsOrNonce), {}));
return this;
}
@@ -83,9 +77,9 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
//
// also supports signing through external signers
public signAsync (account: AddressOrPair, optionsOrNonce: Partial<SignerOptions>): SubmittableThis<ApiType, this> {
return this._decorateMethod(
return decorateMethod(
(): Observable<this> =>
this._signObservable(account, optionsOrNonce).pipe(mapTo(this))
this.#observeSign(account, optionsOrNonce).pipe(mapTo(this))
)();
}
@@ -100,16 +94,16 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
// signAndSend implementation for all 3 cases above
public signAndSend (account: AddressOrPair, optionsOrStatus?: Partial<SignerOptions> | Callback<ISubmittableResult>, optionalStatusCb?: Callback<ISubmittableResult>): SubmittableResultResult<ApiType> | SubmittableResultSubscription<ApiType> {
const [options, statusCb] = this._makeSignAndSendOptions(optionsOrStatus, optionalStatusCb);
const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb);
const [options, statusCb] = this.#makeSignAndSendOptions(optionsOrStatus, optionalStatusCb);
const isSubscription = api.hasSubscriptions && (this.#ignoreStatusCb || !!statusCb);
return this._decorateMethod(
return decorateMethod(
(): Observable<Codec> => (
this._signObservable(account, options).pipe(
this.#observeSign(account, options).pipe(
switchMap((updateId: number | undefined): Observable<ISubmittableResult> | Observable<Hash> =>
isSubscription
? this._subscribeObservable(updateId)
: this._sendObservable(updateId)
? this.#observeSubscribe(updateId)
: this.#observeSend(updateId)
)
) as Observable<Codec>) // FIXME This is wrong, SubmittableResult is _not_ a codec
)(statusCb);
@@ -123,16 +117,65 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
// send implementation for both immediate Hash and statusCb variants
public send (statusCb?: Callback<ISubmittableResult>): SubmittableResultResult<ApiType> | SubmittableResultSubscription<ApiType> {
const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb);
const isSubscription = api.hasSubscriptions && (this.#ignoreStatusCb || !!statusCb);
return this._decorateMethod(
return decorateMethod(
isSubscription
? this._subscribeObservable
: this._sendObservable
? this.#observeSubscribe
: this.#observeSend
)(statusCb);
}
private _makeSignAndSendOptions (optionsOrStatus?: Partial<SignerOptions> | Callback<ISubmittableResult>, statusCb?: Callback<ISubmittableResult>): [Partial<SignerOptions>, Callback<ISubmittableResult>?] {
#getPrelimState = (address: string, options: Partial<SignerOptions>): Observable<[Index, Header | null]> => {
return combineLatest([
// if we have a nonce already, don't retrieve the latest, use what is there
isUndefined(options.nonce)
? api.derive.balances.account(address).pipe(
map(({ accountNonce }): Index => accountNonce)
)
: of(createType(this.registry, 'Index', options.nonce)),
// if we have an era provided already or eraLength is <= 0 (immortal)
// don't get the latest block, just pass null, handle in mergeMap
(isUndefined(options.era) || (isNumber(options.era) && options.era > 0))
? api.rpc.chain.getHeader()
: of(null)
]);
}
#makeEraOptions = (options: Partial<SignerOptions>, { header, nonce }: { header: Header | null; nonce: Index }): SignatureOptions => {
if (!header) {
if (isNumber(options.era)) {
// since we have no header, it is immortal, remove any option overrides
// so we only supply the genesisHash and no era to the construction
delete options.era;
delete options.blockHash;
}
return this.#makeSignOptions(options, { nonce });
}
return this.#makeSignOptions(options, {
blockHash: header.hash,
era: createType(this.registry, 'ExtrinsicEra', {
current: header.number,
period: options.era || DEFAULT_MORTAL_LENGTH
}),
nonce
});
}
#makeSignOptions = (options: Partial<SignerOptions>, extras: { blockHash?: Hash; era?: ExtrinsicEra; nonce?: Index }): SignatureOptions => {
return {
blockHash: api.genesisHash,
...options,
...extras,
genesisHash: api.genesisHash,
runtimeVersion: api.runtimeVersion,
version: api.extrinsicType
} as SignatureOptions;
}
#makeSignAndSendOptions = (optionsOrStatus?: Partial<SignerOptions> | Callback<ISubmittableResult>, statusCb?: Callback<ISubmittableResult>): [Partial<SignerOptions>, Callback<ISubmittableResult>?] => {
let options: Partial<SignerOptions> = {};
if (isFunction(optionsOrStatus)) {
@@ -144,28 +187,81 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
return [options, statusCb];
}
private _signObservable (account: AddressOrPair, optionsOrNonce: Partial<SignerOptions>): Observable<number | undefined> {
#observeSign = (account: AddressOrPair, optionsOrNonce: Partial<SignerOptions>): Observable<number | undefined> => {
const address = isKeyringPair(account) ? account.address : account.toString();
const options = this._optionsOrNonce(optionsOrNonce);
const options = this.#optionsOrNonce(optionsOrNonce);
let updateId: number | undefined;
return this._getPrelimState(address, options).pipe(
return this.#getPrelimState(address, options).pipe(
first(),
mergeMap(async ([nonce, header]): Promise<void> => {
const eraOptions = this._makeEraOptions(options, { header, nonce });
const eraOptions = this.#makeEraOptions(options, { header, nonce });
if (isKeyringPair(account)) {
this.sign(account, eraOptions);
} else {
updateId = await this._signViaSigner(address, eraOptions, header);
updateId = await this.#signViaSigner(address, eraOptions, header);
}
}),
mapTo(updateId)
);
}
private async _signViaSigner (address: Address | string | Uint8Array, options: SignatureOptions, header: Header | null): Promise<number> {
const signer = options.signer || this._api.signer;
#observeStatus = (status: ExtrinsicStatus): Observable<ISubmittableResult> => {
if (!status.isFinalized && !status.isInBlock) {
return of(new SubmittableResult({ status }));
}
const blockHash = status.isInBlock
? status.asInBlock
: status.asFinalized;
return combineLatest([
api.rpc.chain.getBlock(blockHash),
api.query.system.events.at(blockHash)
]).pipe(
map(([signedBlock, allEvents]): ISubmittableResult =>
new SubmittableResult({
events: filterEvents(this.hash, signedBlock, allEvents),
status
})
)
);
}
#observeSend = (updateId = -1): Observable<Hash> => {
return api.rpc.author
.submitExtrinsic(this)
.pipe(
tap((hash): void => {
this.#updateSigner(updateId, hash);
})
);
}
#observeSubscribe = (updateId = -1): Observable<ISubmittableResult> => {
return api.rpc.author
.submitAndWatchExtrinsic(this)
.pipe(
switchMap((status): Observable<ISubmittableResult> =>
this.#observeStatus(status)
),
tap((status): void => {
this.#updateSigner(updateId, status);
})
);
}
// NOTE here we actually override nonce if it was specified (backwards compat for
// the previous signature - don't let userspace break, but allow then time to upgrade)
#optionsOrNonce = (optionsOrNonce: Partial<SignerOptions>): Partial<SignerOptions> => {
return isBn(optionsOrNonce) || isNumber(optionsOrNonce)
? { nonce: optionsOrNonce }
: optionsOrNonce;
}
#signViaSigner = async (address: Address | string | Uint8Array, options: SignatureOptions, header: Header | null): Promise<number> => {
const signer = options.signer || api.signer;
assert(signer, 'No signer specified, either via api.setSigner or via sign options');
@@ -193,119 +289,10 @@ export default function createClass <ApiType extends ApiTypes> ({ api, apiType,
return result.id;
}
private _makeSignOptions (options: Partial<SignerOptions>, extras: { blockHash?: Hash; era?: ExtrinsicEra; nonce?: Index }): SignatureOptions {
return {
blockHash: this._api.genesisHash,
...options,
...extras,
genesisHash: this._api.genesisHash,
runtimeVersion: this._api.runtimeVersion,
version: this._api.extrinsicType
} as SignatureOptions;
}
private _makeEraOptions (options: Partial<SignerOptions>, { header, nonce }: { header: Header | null; nonce: Index }): SignatureOptions {
if (!header) {
if (isNumber(options.era)) {
// since we have no header, it is immortal, remove any option overrides
// so we only supply the genesisHash and no era to the construction
delete options.era;
delete options.blockHash;
}
return this._makeSignOptions(options, { nonce });
#updateSigner = (updateId: number, status: Hash | ISubmittableResult): void => {
if ((updateId !== -1) && api.signer && api.signer.update) {
api.signer.update(updateId, status);
}
return this._makeSignOptions(options, {
blockHash: header.hash,
era: createType(this.registry, 'ExtrinsicEra', {
current: header.number,
period: options.era || DEFAULT_MORTAL_LENGTH
}),
nonce
});
}
private _getPrelimState (address: string, options: Partial<SignerOptions>): Observable<[Index, Header | null]> {
return combineLatest([
// if we have a nonce already, don't retrieve the latest, use what is there
isUndefined(options.nonce)
// FIXME This apparently is having issues on latest Kusama for nonce retrieval,
// hence we are using the accountNonce only
// ? this._api.rpc.account.nextIndex
// ? this._api.rpc.account.nextIndex(address)
// : this._api.query.system.accountNonce(address)
? this._api.query.system.account
? this._api.query.system.account<ITuple<[Index, AccountData]>>(address).pipe(
map(([nonce]): Index => nonce)
)
: this._api.query.system.accountNonce<Index>(address)
: of(createType(this.registry, 'Index', options.nonce)),
// if we have an era provided already or eraLength is <= 0 (immortal)
// don't get the latest block, just pass null, handle in mergeMap
(isUndefined(options.era) || (isNumber(options.era) && options.era > 0))
? this._api.rpc.chain.getHeader()
: of(null)
]);
}
private _updateSigner (updateId: number, status: Hash | ISubmittableResult): void {
if ((updateId !== -1) && this._api.signer && this._api.signer.update) {
this._api.signer.update(updateId, status);
}
}
private _statusObservable (status: ExtrinsicStatus): Observable<ISubmittableResult> {
if (!status.isFinalized && !status.isInBlock) {
return of(new SubmittableResult({ status }));
}
const blockHash = status.isInBlock
? status.asInBlock
: status.asFinalized;
return combineLatest([
this._api.rpc.chain.getBlock(blockHash),
this._api.query.system.events.at(blockHash)
]).pipe(
map(([signedBlock, allEvents]): ISubmittableResult =>
new SubmittableResult({
events: filterEvents(this.hash, signedBlock, allEvents),
status
})
)
);
}
private _sendObservable = (updateId = -1): Observable<Hash> => {
return this._api.rpc.author
.submitExtrinsic(this)
.pipe(
tap((hash): void => {
this._updateSigner(updateId, hash);
})
);
}
private _subscribeObservable = (updateId = -1): Observable<ISubmittableResult> => {
return this._api.rpc.author
.submitAndWatchExtrinsic(this)
.pipe(
switchMap((status): Observable<ISubmittableResult> =>
this._statusObservable(status)
),
tap((status): void => {
this._updateSigner(updateId, status);
})
);
}
// NOTE here we actually override nonce if it was specified (backwards compat for
// the previous signature - don't let userspace break, but allow then time to upgrade)
private _optionsOrNonce = (optionsOrNonce: Partial<SignerOptions>): Partial<SignerOptions> => {
return isBn(optionsOrNonce) || isNumber(optionsOrNonce)
? { nonce: optionsOrNonce }
: optionsOrNonce;
}
};
}
@@ -34,7 +34,7 @@ type MetaAsX = 'asV0' | 'asV1' | 'asV2' | 'asV3' | 'asV4' | 'asV5' | 'asV6' | 'a
* The versioned runtime metadata as a decoded structure
*/
export default class MetadataVersioned extends Struct {
private _converted: Map<number, MetaMapped> = new Map();
readonly #converted: Map<number, MetaMapped> = new Map();
constructor (registry: Registry, value?: any) {
super(registry, {
@@ -59,11 +59,11 @@ export default class MetadataVersioned extends Struct {
return this.metadata[asCurr] as T;
}
if (!this._converted.has(version)) {
this._converted.set(version, fromPrev(this.registry, this[asPrev] as F));
if (!this.#converted.has(version)) {
this.#converted.set(version, fromPrev(this.registry, this[asPrev] as F));
}
return this._converted.get(version) as T;
return this.#converted.get(version) as T;
}
/**
+3 -3
View File
@@ -65,7 +65,7 @@ function createErrorMessage ({ method, params, type }: RpcMethod, error: Error):
* ```
*/
export default class Rpc implements RpcInterface {
private _storageCache = new Map<string, string | null>();
readonly #storageCache = new Map<string, string | null>();
public readonly mapping: Map<string, RpcMethod> = new Map();
@@ -396,7 +396,7 @@ export default class Rpc implements RpcInterface {
// - if a single result value, don't fill - it is not an update hole
// - fallback to an empty option in all cases
const value = isUndefined(found)
? (witCache && this._storageCache.get(hexKey)) || null
? (witCache && this.#storageCache.get(hexKey)) || null
: found[1];
const isEmpty = isNull(value);
const input = isEmpty || this.treatAsHex(key)
@@ -406,7 +406,7 @@ export default class Rpc implements RpcInterface {
// store the retrieved result - the only issue with this cache is that there is no
// clearing of it, so very long running processes (not just a couple of hours, longer)
// will increase memory beyond what is allowed.
this._storageCache.set(hexKey, value);
this.#storageCache.set(hexKey, value);
if (meta.modifier.isOptional) {
return new Option(
+3 -3
View File
@@ -8,7 +8,7 @@ import { assert, isUndefined, isNumber, isString } from '@polkadot/util';
/** @internal */
export default class RpcCoder {
private id = 0;
#id = 0;
public decodeResponse (response: JsonRpcResponse): any {
assert(response, 'Empty response object received');
@@ -39,7 +39,7 @@ export default class RpcCoder {
public encodeObject (method: string, params: any | any[]): JsonRpcRequest {
return {
id: ++this.id,
id: ++this.#id,
jsonrpc: '2.0',
method,
params
@@ -47,7 +47,7 @@ export default class RpcCoder {
}
public getId (): number {
return this.id;
return this.#id;
}
private checkError (error?: JsonRpcResponseBaseError): void {
+7 -7
View File
@@ -36,9 +36,9 @@ const l = logger('api-http');
* @see [[WsProvider]]
*/
export default class HttpProvider implements ProviderInterface {
private coder: Coder;
readonly #coder: Coder;
private endpoint: string;
readonly #endpoint: string;
/**
* @param {string} endpoint The endpoint url starting with http://
@@ -46,8 +46,8 @@ export default class HttpProvider implements ProviderInterface {
constructor (endpoint: string = defaults.HTTP_URL) {
assert(/^(https|http):\/\//.test(endpoint), `Endpoint should start with 'http://', received '${endpoint}'`);
this.coder = new Coder();
this.endpoint = endpoint;
this.#coder = new Coder();
this.#endpoint = endpoint;
}
/**
@@ -95,8 +95,8 @@ export default class HttpProvider implements ProviderInterface {
* @summary Send HTTP POST Request with Body to configured HTTP Endpoint.
*/
public async send (method: string, params: any[]): Promise<any> {
const body = this.coder.encodeJson(method, params);
const response = await fetch(this.endpoint, {
const body = this.#coder.encodeJson(method, params);
const response = await fetch(this.#endpoint, {
body,
headers: {
Accept: 'application/json',
@@ -110,7 +110,7 @@ export default class HttpProvider implements ProviderInterface {
const result = await response.json();
return this.coder.decodeResponse(result);
return this.#coder.decodeResponse(result);
}
/**
+77 -81
View File
@@ -65,25 +65,25 @@ const l = logger('api-ws');
* @see [[HttpProvider]]
*/
export default class WsProvider implements WSProviderInterface {
private _eventemitter: EventEmitter;
readonly #coder: Coder;
private _isConnected = false;
readonly #endpoint: string;
private autoConnect: boolean;
readonly #eventemitter: EventEmitter;
private coder: Coder;
readonly #handlers: Record<string, WsStateAwaiting> = {};
private endpoint: string;
readonly #queued: Record<string, string> = {};
private handlers: Record<string, WsStateAwaiting>;
readonly #waitingForId: Record<string, JsonRpcResponse> = {};
private queued: Record<string, string>;
#autoConnect: boolean;
private subscriptions: Record<string, WsStateSubscription>;
#isConnected = false;
private waitingForId: Record<string, JsonRpcResponse>;
#subscriptions: Record<string, WsStateSubscription> = {};
private websocket: WebSocket | null;
#websocket: WebSocket | null;
/**
* @param {string} endpoint The endpoint url. Usually `ws://ip:9944` or `wss://ip:9944`
@@ -92,15 +92,11 @@ export default class WsProvider implements WSProviderInterface {
constructor (endpoint: string = defaults.WS_URL, autoConnect = true) {
assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
this._eventemitter = new EventEmitter();
this.autoConnect = autoConnect;
this.coder = new Coder();
this.endpoint = endpoint;
this.handlers = {};
this.queued = {};
this.subscriptions = {};
this.waitingForId = {};
this.websocket = null;
this.#eventemitter = new EventEmitter();
this.#autoConnect = autoConnect;
this.#coder = new Coder();
this.#endpoint = endpoint;
this.#websocket = null;
if (autoConnect) {
this.connect();
@@ -118,7 +114,7 @@ export default class WsProvider implements WSProviderInterface {
* @description Returns a clone of the object
*/
public clone (): WsProvider {
return new WsProvider(this.endpoint);
return new WsProvider(this.#endpoint);
}
/**
@@ -128,12 +124,11 @@ export default class WsProvider implements WSProviderInterface {
*/
public connect (): void {
try {
this.websocket = new WebSocket(this.endpoint);
this.websocket.onclose = this.onSocketClose;
this.websocket.onerror = this.onSocketError;
this.websocket.onmessage = this.onSocketMessage;
this.websocket.onopen = this.onSocketOpen;
this.#websocket = new WebSocket(this.#endpoint);
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);
}
@@ -143,16 +138,16 @@ export default class WsProvider implements WSProviderInterface {
* @description Manually disconnect from the connection, clearing autoconnect logic
*/
public disconnect (): void {
if (isNull(this.websocket)) {
if (isNull(this.#websocket)) {
throw new Error('Cannot disconnect on a non-open websocket');
}
// switch off autoConnect, we are in manual mode now
this.autoConnect = false;
this.#autoConnect = false;
// 1000 - Normal closure; the connection successfully completed
this.websocket.close(1000);
this.websocket = null;
this.#websocket.close(1000);
this.#websocket = null;
}
/**
@@ -160,7 +155,7 @@ export default class WsProvider implements WSProviderInterface {
* @return {boolean} true if connected
*/
public isConnected (): boolean {
return this._isConnected;
return this.#isConnected;
}
/**
@@ -170,9 +165,10 @@ export default class WsProvider implements WSProviderInterface {
* @return unsubscribe function
*/
public on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void {
this._eventemitter.on(type, sub);
this.#eventemitter.on(type, sub);
return (): void => {
this._eventemitter.removeListener(type, sub);
this.#eventemitter.removeListener(type, sub);
};
}
@@ -185,8 +181,8 @@ export default class WsProvider implements WSProviderInterface {
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 json = this.#coder.encodeJson(method, params);
const id = this.#coder.getId();
const callback = (error?: Error | null, result?: any): void => {
error
? reject(error)
@@ -195,17 +191,17 @@ export default class WsProvider implements WSProviderInterface {
l.debug((): string[] => ['calling', method, json]);
this.handlers[id] = {
this.#handlers[id] = {
callback,
method,
params,
subscription
};
if (this.isConnected() && !isNull(this.websocket)) {
this.websocket.send(json);
if (this.isConnected() && !isNull(this.#websocket)) {
this.#websocket.send(json);
} else {
this.queued[id] = json;
this.#queued[id] = json;
}
} catch (error) {
reject(error);
@@ -252,55 +248,55 @@ export default class WsProvider implements WSProviderInterface {
// 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])) {
if (isUndefined(this.#subscriptions[subscription])) {
l.debug((): string => `Unable to find active subscription=${subscription}`);
return false;
}
delete this.subscriptions[subscription];
delete this.#subscriptions[subscription];
const result = await this.send(method, [id]);
return result as boolean;
}
private emit (type: ProviderInterfaceEmitted, ...args: any[]): void {
this._eventemitter.emit(type, ...args);
#emit = (type: ProviderInterfaceEmitted, ...args: any[]): void => {
this.#eventemitter.emit(type, ...args);
}
private onSocketClose = (event: CloseEvent): void => {
if (this.autoConnect) {
l.error(`disconnected from ${this.endpoint} code: '${event.code}' reason: '${event.reason}'`);
#onSocketClose = (event: CloseEvent): void => {
if (this.#autoConnect) {
l.error(`disconnected from ${this.#endpoint} code: '${event.code}' reason: '${event.reason}'`);
}
this._isConnected = false;
this.emit('disconnected');
this.#isConnected = false;
this.#emit('disconnected');
if (this.autoConnect) {
if (this.#autoConnect) {
setTimeout((): void => {
this.connect();
}, 1000);
}
}
private onSocketError = (error: Event): void => {
#onSocketError = (error: Event): void => {
l.debug((): any => ['socket error', error]);
this.emit('error', error);
this.#emit('error', error);
}
private onSocketMessage = (message: MessageEvent): void => {
#onSocketMessage = (message: MessageEvent): void => {
l.debug((): any => ['received', message.data]);
const response: JsonRpcResponse = JSON.parse(message.data as string);
return isUndefined(response.method)
? this.onSocketMessageResult(response)
: this.onSocketMessageSubscribe(response);
? this.#onSocketMessageResult(response)
: this.#onSocketMessageSubscribe(response);
}
private onSocketMessageResult = (response: JsonRpcResponse): void => {
const handler = this.handlers[response.id];
#onSocketMessageResult = (response: JsonRpcResponse): void => {
const handler = this.#handlers[response.id];
if (!handler) {
l.debug((): string => `Unable to find handler for id=${response.id}`);
@@ -309,7 +305,7 @@ export default class WsProvider implements WSProviderInterface {
try {
const { method, params, subscription } = handler;
const result = this.coder.decodeResponse(response);
const result = this.#coder.decodeResponse(response);
// first send the result - in case of subs, we may have an update
// immediately if we have some queued results already
@@ -318,42 +314,42 @@ export default class WsProvider implements WSProviderInterface {
if (subscription) {
const subId = `${subscription.type}::${result}`;
this.subscriptions[subId] = {
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]);
if (this.#waitingForId[subId]) {
this.#onSocketMessageSubscribe(this.#waitingForId[subId]);
}
}
} catch (error) {
handler.callback(error, undefined);
}
delete this.handlers[response.id];
delete this.#handlers[response.id];
}
private onSocketMessageSubscribe = (response: JsonRpcResponse): void => {
#onSocketMessageSubscribe = (response: JsonRpcResponse): void => {
const method = ALIASSES[response.method as string] || response.method;
const subId = `${method}::${response.params.subscription}`;
const handler = this.subscriptions[subId];
const handler = this.#subscriptions[subId];
if (!handler) {
// store the JSON, we could have out-of-order subid coming in
this.waitingForId[subId] = response;
this.#waitingForId[subId] = response;
l.debug((): string => `Unable to find handler for subscription=${subId}`);
return;
}
// housekeeping
delete this.waitingForId[subId];
delete this.#waitingForId[subId];
try {
const result = this.coder.decodeResponse(response);
const result = this.#coder.decodeResponse(response);
handler.callback(null, result);
} catch (error) {
@@ -361,24 +357,24 @@ export default class WsProvider implements WSProviderInterface {
}
}
private onSocketOpen = (): boolean => {
assert(!isNull(this.websocket), 'WebSocket cannot be null in onOpen');
#onSocketOpen = (): boolean => {
assert(!isNull(this.#websocket), 'WebSocket cannot be null in onOpen');
l.debug((): any[] => ['connected to', this.endpoint]);
l.debug((): any[] => ['connected to', this.#endpoint]);
this._isConnected = true;
this.emit('connected');
this.#isConnected = true;
this.sendQueue();
this.resubscribe();
this.#emit('connected');
this.#sendQueue();
this.#resubscribe();
return true;
}
private resubscribe (): void {
const subscriptions = this.subscriptions;
#resubscribe = (): void => {
const subscriptions = this.#subscriptions;
this.subscriptions = {};
this.#subscriptions = {};
// eslint-disable-next-line @typescript-eslint/no-misused-promises
Object.keys(subscriptions).forEach(async (id): Promise<void> => {
@@ -399,15 +395,15 @@ export default class WsProvider implements WSProviderInterface {
});
}
private sendQueue (): void {
Object.keys(this.queued).forEach((id): void => {
#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]
(this.#websocket as WebSocket).send(
this.#queued[id]
);
delete this.queued[id];
delete this.#queued[id];
} catch (error) {
l.error(error);
}
+5 -20
View File
@@ -21,38 +21,23 @@ describe('onConnect', (): void => {
it('Does not connect when autoConnect is false', (): void => {
const provider: WsProvider = new WsProvider(TEST_WS_URL, false);
// We need to access the private WsProvider property 'websocket' here which would otherwise trigger a tslint error.
expect((provider as any).websocket).toBeNull();
expect(provider.isConnected()).toBe(false);
});
it('Does connect when autoConnect is true', (): void => {
const provider: WsProvider = new WsProvider(TEST_WS_URL, true);
expect((provider as any).websocket).not.toBeNull();
expect(provider.isConnected()).not.toBe(true);
});
it('Creates a new WebSocket instance by calling the connect() method', (): void => {
const provider: WsProvider = new WsProvider(TEST_WS_URL, false);
expect((provider as any).websocket).toBeNull();
expect(provider.isConnected()).toBe(false);
provider.connect();
expect((provider as any).websocket).not.toBeNull();
expect((provider as any).websocket instanceof WebSocket).toBe(true);
});
it('Creates the on handlers', (): void => {
const provider: WsProvider = new WsProvider(TEST_WS_URL);
expect((provider as any).websocket).not.toBeNull();
expect((provider as any).websocket).toEqual(expect.objectContaining({
listeners: expect.objectContaining({
close: [expect.any(Function)],
error: [expect.any(Function)],
message: [expect.any(Function)],
open: [expect.any(Function)]
})
}));
expect(provider.isConnected()).not.toBe(true);
});
});
@@ -1,30 +0,0 @@
// Copyright 2017-2020 @polkadot/rpc-provider authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import WsProvider from './';
describe('WsProvider', (): void => {
let provider: WsProvider;
beforeEach((): void => {
provider = new WsProvider('ws://127.0.0.1:1234', false);
});
it('calls the handler when found', (done): void => {
// We need to access the private WsProvider property 'handlers' here which otherwise trigger a tslint error..
(provider as any).handlers[5] = {
callback: (_: any, result: any): void => {
expect(result).toEqual('test');
done();
},
method: '',
params: []
};
// We need to access the private WsProvider property 'onSocketMessage' here which would otherwise trigger a tslint error.
(provider as any).onSocketMessage(
new MessageEvent('test', { data: '{"jsonrpc":"2.0","id":5,"result":"test"}' })
);
});
});
@@ -1,57 +0,0 @@
// Copyright 2017-2020 @polkadot/rpc-provider authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import WsProvider from './';
describe('onMessageSubscribe', (): void => {
let provider: WsProvider;
beforeEach((): void => {
provider = new WsProvider('ws://127.0.0.1:1234', false);
});
it('calls the subscriber with data', (done): void => {
// We need to access the private WsProvider property 'handlers' here which otherwise triggers a tslint error..
(provider as any).handlers[11] = {
callback: (_: any, id: number): void => {
expect(typeof id).toBe('number');
},
method: 'test',
params: [],
subscription: {
callback: (_: any, result: any): void => {
expect(result).toEqual('test');
done();
},
type: 'test'
}
};
// We need to access the private WsProvider property 'onSocketMessage' here which would otherwise trigger a tslint error.
(provider as any).onSocketMessage(new MessageEvent('test', { data: '{"jsonrpc":"2.0","id":11,"result":22}' }));
(provider as any).onSocketMessage(new MessageEvent('test', { data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"result":"test"}}' }));
});
it('calls the subscriber with error', (done): void => {
// We need to access the private WsProvider property 'handlers' here which otherwise trigger a tslint error..
(provider as any).handlers[11] = {
callback: (_: any, id: number): void => {
expect(typeof id).toBe('number');
},
method: 'test',
params: [],
subscription: {
callback: (error: any): void => {
expect(error.message).toMatch(/test/);
done();
},
type: 'test'
}
};
// We need to access the private WsProvider property 'onSocketMessage' here which would otherwise trigger a tslint error.
(provider as any).onSocketMessage(new MessageEvent('test', { data: '{"jsonrpc":"2.0","id":11,"result":22}' }));
(provider as any).onSocketMessage(new MessageEvent('test', { data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"error":{"message":"test"}}}' }));
});
});
+8 -13
View File
@@ -39,21 +39,16 @@ describe('send', (): void => {
});
it('handles internal errors', (): Promise<any> => {
createMock([]);
(global as any).WebSocket = class {
public send (): void {
throw new Error('send error');
createMock([{
id: 1,
method: 'test_body',
reply: {
result: 'ok'
}
};
}]);
provider = createWs(true);
// HACK this is a private method...
(provider as any).websocket.onopen();
return provider
.send('test_encoding', ['param'])
return createWs(true)
.send('test_encoding', [{ error: 'send error' }])
.catch((error): void => {
expect(error.message).toEqual('send error');
});
+12 -12
View File
@@ -30,19 +30,19 @@ export function isBigInt (value: any): value is BigInt {
export default abstract class AbstractInt extends BN implements Codec {
public readonly registry: Registry;
protected _bitLength: UIntBitLength;
readonly #bitLength: UIntBitLength;
private _isHexJson: boolean;
readonly #isHexJson: boolean;
private _isSigned: boolean;
readonly #isSigned: boolean;
protected constructor (registry: Registry, isSigned: boolean, value: AnyNumber = 0, bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson = true) {
super(AbstractInt.decodeAbstracInt(value, bitLength, isSigned));
this.registry = registry;
this._bitLength = bitLength;
this._isHexJson = isHexJson;
this._isSigned = isSigned;
this.#bitLength = bitLength;
this.#isHexJson = isHexJson;
this.#isSigned = isSigned;
assert(super.bitLength() <= bitLength, `${this.toRawType()}: Input too large. Found input with ${super.bitLength()} bits, expected ${bitLength}`);
}
@@ -83,7 +83,7 @@ export default abstract class AbstractInt extends BN implements Codec {
* @description The length of the value when encoded as a Uint8Array
*/
public get encodedLength (): number {
return this._bitLength / 8;
return this.#bitLength / 8;
}
/**
@@ -104,14 +104,14 @@ export default abstract class AbstractInt extends BN implements Codec {
* @description Checks if the value is an unsigned type
*/
public get isUnsigned (): boolean {
return !this._isSigned;
return !this.#isSigned;
}
/**
* @description Returns the number of bits in the value
*/
public bitLength (): UIntBitLength {
return this._bitLength;
return this.#bitLength;
}
/**
@@ -123,7 +123,7 @@ export default abstract class AbstractInt extends BN implements Codec {
// number and BN inputs (no `.eqn` needed) - numbers will be converted
return super.eq(
isHex(other)
? hexToBn(other.toString(), { isLe: false, isNegative: this._isSigned })
? hexToBn(other.toString(), { isLe: false, isNegative: this.#isSigned })
: bnToBn(other)
);
}
@@ -134,7 +134,7 @@ export default abstract class AbstractInt extends BN implements Codec {
public isMax (): boolean {
const u8a = this.toU8a().filter((byte): boolean => byte === 0xff);
return u8a.length === (this._bitLength / 8);
return u8a.length === (this.#bitLength / 8);
}
/**
@@ -164,7 +164,7 @@ export default abstract class AbstractInt extends BN implements Codec {
// FIXME this return type should by string | number, but BN's return type
// is string.
// Maximum allowed integer for JS is 2^53 - 1, set limit at 52
return this._isHexJson || (super.bitLength() > 52)
return this.#isHexJson || (super.bitLength() > 52)
? this.toHex()
: this.toNumber();
}
+2 -2
View File
@@ -15,9 +15,9 @@ import { compareMap, decodeU8a, typeToConstructor } from './utils';
export default class BTreeMap<K extends Codec = Codec, V extends Codec = Codec> extends Map<K, V> implements Codec {
public readonly registry: Registry;
protected _KeyClass: Constructor<K>;
private readonly _KeyClass: Constructor<K>;
protected _ValClass: Constructor<V>;
private readonly _ValClass: Constructor<V>;
constructor (registry: Registry, keyType: Constructor<K> | InterfaceTypes, valType: Constructor<V> | InterfaceTypes, rawValue: any) {
const KeyClass = typeToConstructor(registry, keyType);
+1 -1
View File
@@ -15,7 +15,7 @@ import { compareSet, decodeU8a, typeToConstructor } from './utils';
export default class BTreeSet<V extends Codec = Codec> extends Set<V> implements Codec {
public readonly registry: Registry;
protected _ValClass: Constructor<V>;
private readonly _ValClass: Constructor<V>;
constructor (registry: Registry, valType: Constructor<V> | InterfaceTypes, rawValue: any) {
const ValClass = typeToConstructor(registry, valType);
+1 -1
View File
@@ -16,7 +16,7 @@ import Raw from './Raw';
export default abstract class Base<T extends Codec> implements Codec {
public readonly registry: Registry;
protected raw: T;
protected readonly raw: T;
protected constructor (registry: Registry, value?: any) {
this.registry = registry;
-3
View File
@@ -27,13 +27,10 @@ const BITLENGTH: UIntBitLength = 64;
export default class CodecDate extends Date implements Codec {
public readonly registry: Registry;
protected raw: Date; // FIXME Remove this once we convert all types out of Base
constructor (registry: Registry, value: CodecDate | Date | AnyNumber = 0) {
super(CodecDate.decodeDate(value));
this.registry = registry;
this.raw = this;
}
/** @internal */
+3 -3
View File
@@ -40,7 +40,7 @@ export default class Int extends AbstractInt {
*/
public toHex (isLe = false): string {
return bnToHex(this, {
bitLength: this._bitLength,
bitLength: this.bitLength(),
isLe,
isNegative: true
});
@@ -50,7 +50,7 @@ export default class Int extends AbstractInt {
* @description Returns the base runtime type name for this instance
*/
public toRawType (): string {
return `i${this._bitLength}`;
return `i${this.bitLength()}`;
}
/**
@@ -60,7 +60,7 @@ export default class Int extends AbstractInt {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public toU8a (isBare?: boolean): Uint8Array {
return bnToU8a(this, {
bitLength: this._bitLength,
bitLength: this.bitLength(),
isLe: true,
isNegative: true
});
+3 -3
View File
@@ -42,7 +42,7 @@ export default class UInt extends AbstractInt {
public toHex (isLe = false): string {
// For display/JSON, this is BE, for compare, use isLe
return bnToHex(this, {
bitLength: this._bitLength,
bitLength: this.bitLength(),
isLe,
isNegative: false
});
@@ -67,7 +67,7 @@ export default class UInt extends AbstractInt {
// underlying it always matches (no matter which length it actually is)
return this instanceof ClassOf(this.registry, 'Balance')
? 'Balance'
: `u${this._bitLength}`;
: `u${this.bitLength()}`;
}
/**
@@ -77,7 +77,7 @@ export default class UInt extends AbstractInt {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public toU8a (isBare?: boolean): Uint8Array {
return bnToU8a(this, {
bitLength: this._bitLength,
bitLength: this.bitLength(),
isLe: true,
isNegative: false
});
+40 -40
View File
@@ -77,21 +77,21 @@ function decorateExtrinsics (registry: Registry, metadata: RegistryMetadata, met
}
export class TypeRegistry implements Registry {
private _chainProperties?: ChainProperties;
readonly #classes: Map<string, Constructor> = new Map();
private _classes: Map<string, Constructor> = new Map();
readonly #definitions: Map<string, string> = new Map();
private _definitions: Map<string, string> = new Map();
readonly #metadataCalls: Record<string, CallFunction> = {};
private _metadataCalls: Record<string, CallFunction> = {};
readonly #metadataErrors: Record<string, RegistryError> = {};
private _metadataErrors: Record<string, RegistryError> = {};
readonly #metadataEvents: Record<string, Constructor<EventData>> = {};
private _metadataEvents: Record<string, Constructor<EventData>> = {};
readonly #unknownTypes: Map<string, boolean> = new Map();
private _metadataExtensions: string[] = defaultExtensions;
#chainProperties?: ChainProperties;
private _unknownTypes: Map<string, boolean> = new Map();
#metadataExtensions: string[] = defaultExtensions;
constructor () {
// we only want to import these on creation, i.e. we want to avoid types
@@ -110,27 +110,27 @@ export class TypeRegistry implements Registry {
}
public get chainDecimals (): number {
return this._chainProperties?.tokenDecimals.isSome
? this._chainProperties.tokenDecimals.unwrap().toNumber()
return this.#chainProperties?.tokenDecimals.isSome
? this.#chainProperties.tokenDecimals.unwrap().toNumber()
: 12;
}
public get chainSS58 (): number | undefined {
return this._chainProperties?.ss58Format.isSome
? this._chainProperties.ss58Format.unwrap().toNumber()
return this.#chainProperties?.ss58Format.isSome
? this.#chainProperties.ss58Format.unwrap().toNumber()
: undefined;
}
public get chainToken (): string {
return this._chainProperties?.tokenSymbol.isSome
? this._chainProperties.tokenSymbol.unwrap().toString()
return this.#chainProperties?.tokenSymbol.isSome
? this.#chainProperties.tokenSymbol.unwrap().toString()
: formatBalance.getDefaults().unit;
}
// find a specific call
public findMetaCall (callIndex: Uint8Array): CallFunction {
const hexIndex = u8aToHex(callIndex);
const fn = this._metadataCalls[hexIndex];
const fn = this.#metadataCalls[hexIndex];
assert(!isUndefined(fn), `findMetaCall: Unable to find Call with index ${hexIndex}/[${callIndex}]`);
@@ -144,7 +144,7 @@ export class TypeRegistry implements Registry {
? errorIndex
: new Uint8Array([errorIndex.index.toNumber(), errorIndex.error.toNumber()])
);
const error = this._metadataErrors[hexIndex];
const error = this.#metadataErrors[hexIndex];
assert(!isUndefined(error), `findMetaError: Unable to find Error with index ${hexIndex}/[${errorIndex}]`);
@@ -153,7 +153,7 @@ export class TypeRegistry implements Registry {
public findMetaEvent (eventIndex: Uint8Array): Constructor<EventData> {
const hexIndex = u8aToHex(eventIndex);
const Event = this._metadataEvents[hexIndex];
const Event = this.#metadataEvents[hexIndex];
assert(!isUndefined(Event), `findMetaEvent: Unable to find Event with index ${hexIndex}/[${eventIndex}]`);
@@ -161,11 +161,11 @@ export class TypeRegistry implements Registry {
}
public get <T extends Codec = Codec> (name: string, withUnknown?: boolean): Constructor<T> | undefined {
let Type = this._classes.get(name);
let Type = this.#classes.get(name);
// we have not already created the type, attempt it
if (!Type) {
const definition = this._definitions.get(name);
const definition = this.#definitions.get(name);
let BaseType: Constructor<Codec> | undefined;
// we have a definition, so create the class now (lazily)
@@ -174,7 +174,7 @@ export class TypeRegistry implements Registry {
} else if (withUnknown) {
console.warn(`Unable to resolve type ${name}, it will fail on constrution`);
this._unknownTypes.set(name, true);
this.#unknownTypes.set(name, true);
BaseType = Unconstructable.with(name);
}
@@ -184,7 +184,7 @@ export class TypeRegistry implements Registry {
// Additionally, we now pass through the registry, which is a link to ourselves
Type = class extends BaseType {};
this._classes.set(name, Type);
this.#classes.set(name, Type);
}
}
@@ -192,11 +192,11 @@ export class TypeRegistry implements Registry {
}
public getChainProperties (): ChainProperties | undefined {
return this._chainProperties;
return this.#chainProperties;
}
public getDefinition (name: string): string | undefined {
return this._definitions.get(name);
return this.#definitions.get(name);
}
public getOrThrow <T extends Codec = Codec> (name: string, msg?: string): Constructor<T> {
@@ -214,23 +214,23 @@ export class TypeRegistry implements Registry {
}
public getSignedExtensionExtra (): Record<string, InterfaceTypes> {
return expandExtensionTypes(this._metadataExtensions, 'extra');
return expandExtensionTypes(this.#metadataExtensions, 'extra');
}
public getSignedExtensionTypes (): Record<string, InterfaceTypes> {
return expandExtensionTypes(this._metadataExtensions, 'types');
return expandExtensionTypes(this.#metadataExtensions, 'types');
}
public hasClass (name: string): boolean {
return this._classes.has(name);
return this.#classes.has(name);
}
public hasDef (name: string): boolean {
return this._definitions.has(name);
return this.#definitions.has(name);
}
public hasType (name: string): boolean {
return !this._unknownTypes.get(name) && (this.hasClass(name) || this.hasDef(name));
return !this.#unknownTypes.get(name) && (this.hasClass(name) || this.hasDef(name));
}
public register (type: Constructor | RegistryTypes): void;
@@ -242,11 +242,11 @@ export class TypeRegistry implements Registry {
public register (arg1: string | Constructor | RegistryTypes, arg2?: Constructor): void {
// NOTE Constructors appear as functions here
if (isFunction(arg1)) {
this._classes.set(arg1.name, arg1);
this.#classes.set(arg1.name, arg1);
} else if (isString(arg1)) {
assert(isFunction(arg2), `Expected class definition passed to '${arg1}' registration`);
this._classes.set(arg1, arg2);
this.#classes.set(arg1, arg2);
} else {
this.registerObject(arg1);
}
@@ -256,18 +256,18 @@ export class TypeRegistry implements Registry {
Object.entries(obj).forEach(([name, type]): void => {
if (isFunction(type)) {
// This _looks_ a bit funny, but `typeof Clazz === 'function'
this._classes.set(name, type);
this.#classes.set(name, type);
} else {
const def = isString(type)
? type
: JSON.stringify(type);
// we already have this type, remove the classes registered for it
if (this._classes.has(name)) {
this._classes.delete(name);
if (this.#classes.has(name)) {
this.#classes.delete(name);
}
this._definitions.set(name, def);
this.#definitions.set(name, def);
}
});
}
@@ -275,22 +275,22 @@ export class TypeRegistry implements Registry {
// sets the chain properties
public setChainProperties (properties?: ChainProperties): void {
if (properties) {
this._chainProperties = properties;
this.#chainProperties = properties;
}
}
// sets the metadata
public setMetadata (metadata: RegistryMetadata): void {
decorateExtrinsics(this, metadata, this._metadataCalls);
decorateErrors(this, metadata, this._metadataErrors);
decorateEvents(this, metadata, this._metadataEvents);
decorateExtrinsics(this, metadata, this.#metadataCalls);
decorateErrors(this, metadata, this.#metadataErrors);
decorateEvents(this, metadata, this.#metadataEvents);
// setup the available extensions
this._metadataExtensions = metadata.asLatest.extrinsic.version.gtn(0)
this.#metadataExtensions = metadata.asLatest.extrinsic.version.gtn(0)
? metadata.asLatest.extrinsic.signedExtensions.map((key): string => key.toString())
: defaultExtensions;
const unknown = findUnknownExtensions(this._metadataExtensions);
const unknown = findUnknownExtensions(this.#metadataExtensions);
if (unknown.length) {
console.warn(`Unknown signed extensions [${unknown.join(', ')}] found, treating them as no-efect`);
+12 -12
View File
@@ -17,49 +17,49 @@ import Null from '../primitive/Null';
* Wrapper for the actual data that forms part of an [[Event]]
*/
export class EventData extends Tuple {
private _meta: EventMetadataLatest;
readonly #meta: EventMetadataLatest;
private _method: string;
readonly #method: string;
private _section: string;
readonly #section: string;
private _typeDef: TypeDef[];
readonly #typeDef: TypeDef[];
constructor (registry: Registry, Types: Constructor[], value: Uint8Array, typeDef: TypeDef[], meta: RegistryMetadataEvent, section: string, method: string) {
super(registry, Types, value);
this._meta = meta as EventMetadataLatest;
this._method = method;
this._section = section;
this._typeDef = typeDef;
this.#meta = meta as EventMetadataLatest;
this.#method = method;
this.#section = section;
this.#typeDef = typeDef;
}
/**
* @description The wrapped [[EventMetadata]]
*/
public get meta (): EventMetadataLatest {
return this._meta;
return this.#meta;
}
/**
* @description The method as a string
*/
public get method (): string {
return this._method;
return this.#method;
}
/**
* @description The section as a string
*/
public get section (): string {
return this._section;
return this.#section;
}
/**
* @description The [[TypeDef]] for this event
*/
public get typeDef (): TypeDef[] {
return this._typeDef;
return this.#typeDef;
}
}
+1
View File
@@ -4,6 +4,7 @@
import { detectPackage } from '@polkadot/util';
// FIXME This really should be `import(...).then(...)`, but need to check rejections
// eslint-disable-next-line @typescript-eslint/no-var-requires
detectPackage(require('./package.json'), __dirname);
+4 -3
View File
@@ -50,7 +50,8 @@ function decodeText (value: Text | string | AnyU8a | { toString: () => string })
// - Strings should probably be trimmed (docs do come through with extra padding)
export default class Text extends String implements Codec {
public readonly registry: Registry;
private _override: string | null = null;
#override: string | null = null;
constructor (registry: Registry, value: Text | string | AnyU8a | { toString: () => string } = '') {
super(decodeText(value));
@@ -100,7 +101,7 @@ export default class Text extends String implements Codec {
* @description Set an override value for this
*/
public setOverride (override: string): void {
this._override = override;
this.#override = override;
}
/**
@@ -137,7 +138,7 @@ export default class Text extends String implements Codec {
* @description Returns the string representation of the value
*/
public toString (): string {
return this._override || super.toString();
return this.#override || super.toString();
}
/**
+14 -4
View File
@@ -416,6 +416,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
"@babel/plugin-syntax-import-meta@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.8.3.tgz#230afff79d3ccc215b5944b438e4e266daf3d84d"
integrity sha512-vYiGd4wQ9gx0Lngb7+bPCwQXGK/PR6FeTIJ+TIOlq+OfOKG/kCAOO2+IBac3oMM9qV7/fU76hfcqxUaLKZf1hQ==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-syntax-json-strings@^7.8.0":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
@@ -1967,15 +1974,16 @@
dependencies:
"@types/node" ">= 8"
"@polkadot/dev@^0.40.22":
version "0.40.22"
resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.40.22.tgz#6306f3d0400180f57bdfa63ce63ee2e9da96d3f8"
integrity sha512-c1m1rk4FFXq/Jy67CzJkZLNtB7agjrB4Tbbwum7WItcdb6GrjnWB32ZmHEBPd982CTyiBRi5Y2OoUZMp6uR13A==
"@polkadot/dev@^0.41.1":
version "0.41.1"
resolved "https://registry.yarnpkg.com/@polkadot/dev/-/dev-0.41.1.tgz#00585db9d4a78b143c7a9572636fb404dbe9de27"
integrity sha512-DWmXD1WDDX/xUfLoF1r/dP9E2158iPHSOEURfnyZrvBgMNYh5FqFZ7dO4sMPebZlHeg5jaK/GzhR0kKr78s8SA==
dependencies:
"@babel/cli" "^7.8.4"
"@babel/core" "^7.8.4"
"@babel/plugin-proposal-class-properties" "^7.8.3"
"@babel/plugin-proposal-decorators" "^7.8.3"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3"
"@babel/plugin-proposal-numeric-separator" "^7.8.3"
"@babel/plugin-proposal-object-rest-spread" "^7.8.3"
"@babel/plugin-proposal-optional-chaining" "^7.8.3"
@@ -1983,6 +1991,8 @@
"@babel/plugin-proposal-private-methods" "^7.8.3"
"@babel/plugin-syntax-bigint" "^7.8.3"
"@babel/plugin-syntax-dynamic-import" "^7.8.3"
"@babel/plugin-syntax-import-meta" "^7.8.3"
"@babel/plugin-syntax-top-level-await" "^7.8.3"
"@babel/plugin-transform-runtime" "^7.8.3"
"@babel/preset-env" "^7.8.4"
"@babel/preset-react" "^7.8.3"