diff --git a/package.json b/package.json index 66825e4f01..429b489efd 100644 --- a/package.json +++ b/package.json @@ -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" } diff --git a/packages/api-derive/src/type/HeaderExtended.ts b/packages/api-derive/src/type/HeaderExtended.ts index cf0798c2e8..fbade1ba1b 100644 --- a/packages/api-derive/src/type/HeaderExtended.ts +++ b/packages/api-derive/src/type/HeaderExtended.ts @@ -17,12 +17,12 @@ const _Header: Constructor
= 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; } /** diff --git a/packages/api-derive/src/type/ReferendumInfoExtended.ts b/packages/api-derive/src/type/ReferendumInfoExtended.ts index 4d2bf7da29..986a3c1af7 100644 --- a/packages/api-derive/src/type/ReferendumInfoExtended.ts +++ b/packages/api-derive/src/type/ReferendumInfoExtended.ts @@ -18,12 +18,12 @@ const _ReferendumInfo: Constructor = 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; } /** diff --git a/packages/api/src/base/Events.ts b/packages/api/src/base/Events.ts index 1ce7139915..c58bb1cb7f 100644 --- a/packages/api/src/base/Events.ts +++ b/packages/api/src/base/Events.ts @@ -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; } diff --git a/packages/api/src/base/Init.ts b/packages/api/src/base/Init.ts index 3a37eebec5..dc70556795 100644 --- a/packages/api/src/base/Init.ts +++ b/packages/api/src/base/Init.ts @@ -20,9 +20,9 @@ const KEEPALIVE_INTERVAL = 15000; const l = logger('api/init'); export default abstract class Init extends Decorate { - private _healthTimer: NodeJS.Timeout | null = null; + #healthTimer: NodeJS.Timeout | null = null; - private _updateSub?: Subscription; + #updateSub?: Subscription; constructor (options: ApiOptions, type: ApiTypes, decorateMethod: DecorateMethod) { super(options, type, decorateMethod); @@ -44,15 +44,15 @@ export default abstract class Init extends Decorate extends Decorate extends Decorate combineLatest( of(version), @@ -201,7 +201,7 @@ export default abstract class Init extends Decorate => { + #onProviderConnect = async (): Promise => { this.emit('connected'); this._isConnected.next(true); @@ -217,7 +217,7 @@ export default abstract class Init extends Decorate { + this.#healthTimer = setInterval((): void => { this._rpcCore.system.health().toPromise().catch((): void => { // ignore }); @@ -231,17 +231,17 @@ export default abstract class Init extends Decorate { + #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); }; } diff --git a/packages/api/src/base/index.ts b/packages/api/src/base/index.ts index 294e1d8860..faf28fefca 100644 --- a/packages/api/src/base/index.ts +++ b/packages/api/src/base/index.ts @@ -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 (value: T | undefined): T { assert(!isUndefined(value), 'Api needs to be initialized before using, listen on \'ready\''); diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 8db99394b7..af9461ec0c 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -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); diff --git a/packages/api/src/promise/Api.ts b/packages/api/src/promise/Api.ts index 5376261868..7119e0aed4 100644 --- a/packages/api/src/promise/Api.ts +++ b/packages/api/src/promise/Api.ts @@ -173,7 +173,7 @@ export function decorateMethod (method: Method, opti * ``` */ export default class ApiPromise extends ApiBase<'promise'> { - private _isReadyPromise: Promise; + #isReadyPromise: Promise; /** * @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 { - return this._isReadyPromise; + return this.#isReadyPromise; } /** diff --git a/packages/api/src/promise/Combinator.ts b/packages/api/src/promise/Combinator.ts index 2e27520d8b..0c1c1ef743 100644 --- a/packages/api/src/promise/Combinator.ts +++ b/packages/api/src/promise/Combinator.ts @@ -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 => { + this.#subscriptions.forEach(async (subscription): Promise => { try { const unsubscribe = await subscription; diff --git a/packages/api/src/rx/Api.ts b/packages/api/src/rx/Api.ts index 103f76132a..933c5073ef 100644 --- a/packages/api/src/rx/Api.ts +++ b/packages/api/src/rx/Api.ts @@ -114,7 +114,7 @@ export function decorateMethod (method: Method): Me * ``` */ export default class ApiRx extends ApiBase<'rxjs'> { - private _isReadyRx: Observable; + #isReadyRx: Observable; /** * @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 { - return this._isReadyRx; + return this.#isReadyRx; } /** diff --git a/packages/api/src/submittable/createClass.ts b/packages/api/src/submittable/createClass.ts index 2da25936e3..acfd2564b2 100644 --- a/packages/api/src/submittable/createClass.ts +++ b/packages/api/src/submittable/createClass.ts @@ -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 ({ api, apiType, const Extrinsic = ClassOf(api.registry, 'Extrinsic'); return class Submittable extends Extrinsic implements SubmittableExtrinsic { - private readonly _api: ApiInterfaceRx; - - private readonly _decorateMethod: ApiBase['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): SubmittablePaymentResult { - const [allOptions] = this._makeSignAndSendOptions(options); + const [allOptions] = this.#makeSignAndSendOptions(options); const address = isKeyringPair(account) ? account.address : account.toString(); - return this._decorateMethod( + return decorateMethod( (): Observable => - this._getPrelimState(address, allOptions).pipe( + this.#getPrelimState(address, allOptions).pipe( first(), switchMap(([nonce, header]): Observable => { // 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 ({ api, apiType, // sign a transaction, returning the this to allow chaining, i.e. .sign(...).send() public sign (account: IKeyringPair, optionsOrNonce: Partial): 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 ({ api, apiType, // // also supports signing through external signers public signAsync (account: AddressOrPair, optionsOrNonce: Partial): SubmittableThis { - return this._decorateMethod( + return decorateMethod( (): Observable => - this._signObservable(account, optionsOrNonce).pipe(mapTo(this)) + this.#observeSign(account, optionsOrNonce).pipe(mapTo(this)) )(); } @@ -100,16 +94,16 @@ export default function createClass ({ api, apiType, // signAndSend implementation for all 3 cases above public signAndSend (account: AddressOrPair, optionsOrStatus?: Partial | Callback, optionalStatusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { - 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 => ( - this._signObservable(account, options).pipe( + this.#observeSign(account, options).pipe( switchMap((updateId: number | undefined): Observable | Observable => isSubscription - ? this._subscribeObservable(updateId) - : this._sendObservable(updateId) + ? this.#observeSubscribe(updateId) + : this.#observeSend(updateId) ) ) as Observable) // FIXME This is wrong, SubmittableResult is _not_ a codec )(statusCb); @@ -123,16 +117,65 @@ export default function createClass ({ api, apiType, // send implementation for both immediate Hash and statusCb variants public send (statusCb?: Callback): SubmittableResultResult | SubmittableResultSubscription { - 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 | Callback, statusCb?: Callback): [Partial, Callback?] { + #getPrelimState = (address: string, options: Partial): Observable<[Index, Header | null]> => { + return combineLatest([ + // if we have a nonce already, don't retrieve the latest, use what is there + isUndefined(options.nonce) + ? api.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, { 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, 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 | Callback, statusCb?: Callback): [Partial, Callback?] => { let options: Partial = {}; if (isFunction(optionsOrStatus)) { @@ -144,28 +187,81 @@ export default function createClass ({ api, apiType, return [options, statusCb]; } - private _signObservable (account: AddressOrPair, optionsOrNonce: Partial): Observable { + #observeSign = (account: AddressOrPair, optionsOrNonce: Partial): Observable => { 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 => { - 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 { - const signer = options.signer || this._api.signer; + #observeStatus = (status: ExtrinsicStatus): Observable => { + 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 => { + return api.rpc.author + .submitExtrinsic(this) + .pipe( + tap((hash): void => { + this.#updateSigner(updateId, hash); + }) + ); + } + + #observeSubscribe = (updateId = -1): Observable => { + return api.rpc.author + .submitAndWatchExtrinsic(this) + .pipe( + switchMap((status): Observable => + 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): Partial => { + return isBn(optionsOrNonce) || isNumber(optionsOrNonce) + ? { nonce: optionsOrNonce } + : optionsOrNonce; + } + + #signViaSigner = async (address: Address | string | Uint8Array, options: SignatureOptions, header: Header | null): Promise => { + 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 ({ api, apiType, return result.id; } - private _makeSignOptions (options: Partial, 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, { 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): 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>(address).pipe( - map(([nonce]): Index => nonce) - ) - : this._api.query.system.accountNonce(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 { - 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 => { - return this._api.rpc.author - .submitExtrinsic(this) - .pipe( - tap((hash): void => { - this._updateSigner(updateId, hash); - }) - ); - } - - private _subscribeObservable = (updateId = -1): Observable => { - return this._api.rpc.author - .submitAndWatchExtrinsic(this) - .pipe( - switchMap((status): Observable => - 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): Partial => { - return isBn(optionsOrNonce) || isNumber(optionsOrNonce) - ? { nonce: optionsOrNonce } - : optionsOrNonce; } }; } diff --git a/packages/metadata/src/Metadata/MetadataVersioned.ts b/packages/metadata/src/Metadata/MetadataVersioned.ts index 1f76db7d61..5651ce2a10 100644 --- a/packages/metadata/src/Metadata/MetadataVersioned.ts +++ b/packages/metadata/src/Metadata/MetadataVersioned.ts @@ -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 = new Map(); + readonly #converted: Map = 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; } /** diff --git a/packages/rpc-core/src/index.ts b/packages/rpc-core/src/index.ts index d4e6648b39..9e428612ba 100644 --- a/packages/rpc-core/src/index.ts +++ b/packages/rpc-core/src/index.ts @@ -65,7 +65,7 @@ function createErrorMessage ({ method, params, type }: RpcMethod, error: Error): * ``` */ export default class Rpc implements RpcInterface { - private _storageCache = new Map(); + readonly #storageCache = new Map(); public readonly mapping: Map = 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( diff --git a/packages/rpc-provider/src/coder/index.ts b/packages/rpc-provider/src/coder/index.ts index 5fc1a09bb3..52d3a4b488 100644 --- a/packages/rpc-provider/src/coder/index.ts +++ b/packages/rpc-provider/src/coder/index.ts @@ -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 { diff --git a/packages/rpc-provider/src/http/index.ts b/packages/rpc-provider/src/http/index.ts index 02a603c1e5..e2bba748c4 100644 --- a/packages/rpc-provider/src/http/index.ts +++ b/packages/rpc-provider/src/http/index.ts @@ -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 { - 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); } /** diff --git a/packages/rpc-provider/src/ws/Provider.ts b/packages/rpc-provider/src/ws/Provider.ts index 45be3266a2..592b1c10d3 100644 --- a/packages/rpc-provider/src/ws/Provider.ts +++ b/packages/rpc-provider/src/ws/Provider.ts @@ -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 = {}; - private endpoint: string; + readonly #queued: Record = {}; - private handlers: Record; + readonly #waitingForId: Record = {}; - private queued: Record; + #autoConnect: boolean; - private subscriptions: Record; + #isConnected = false; - private waitingForId: Record; + #subscriptions: Record = {}; - 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 { 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 => { @@ -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); } diff --git a/packages/rpc-provider/src/ws/connect.spec.ts b/packages/rpc-provider/src/ws/connect.spec.ts index 0e463a3c8f..f8f0d86c75 100644 --- a/packages/rpc-provider/src/ws/connect.spec.ts +++ b/packages/rpc-provider/src/ws/connect.spec.ts @@ -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); }); }); diff --git a/packages/rpc-provider/src/ws/onMessageResult.spec.ts b/packages/rpc-provider/src/ws/onMessageResult.spec.ts deleted file mode 100644 index 62709cdc3c..0000000000 --- a/packages/rpc-provider/src/ws/onMessageResult.spec.ts +++ /dev/null @@ -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"}' }) - ); - }); -}); diff --git a/packages/rpc-provider/src/ws/onMessageSubscribe.spec.ts b/packages/rpc-provider/src/ws/onMessageSubscribe.spec.ts deleted file mode 100644 index 6b2f2ca886..0000000000 --- a/packages/rpc-provider/src/ws/onMessageSubscribe.spec.ts +++ /dev/null @@ -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"}}}' })); - }); -}); diff --git a/packages/rpc-provider/src/ws/send.spec.ts b/packages/rpc-provider/src/ws/send.spec.ts index 4d655dd742..e9184409fc 100644 --- a/packages/rpc-provider/src/ws/send.spec.ts +++ b/packages/rpc-provider/src/ws/send.spec.ts @@ -39,21 +39,16 @@ describe('send', (): void => { }); it('handles internal errors', (): Promise => { - 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'); }); diff --git a/packages/types/src/codec/AbstractInt.ts b/packages/types/src/codec/AbstractInt.ts index 90ac61277b..e532a100d4 100644 --- a/packages/types/src/codec/AbstractInt.ts +++ b/packages/types/src/codec/AbstractInt.ts @@ -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(); } diff --git a/packages/types/src/codec/BTreeMap.ts b/packages/types/src/codec/BTreeMap.ts index 8557d7d7ee..56d47f720c 100644 --- a/packages/types/src/codec/BTreeMap.ts +++ b/packages/types/src/codec/BTreeMap.ts @@ -15,9 +15,9 @@ import { compareMap, decodeU8a, typeToConstructor } from './utils'; export default class BTreeMap extends Map implements Codec { public readonly registry: Registry; - protected _KeyClass: Constructor; + private readonly _KeyClass: Constructor; - protected _ValClass: Constructor; + private readonly _ValClass: Constructor; constructor (registry: Registry, keyType: Constructor | InterfaceTypes, valType: Constructor | InterfaceTypes, rawValue: any) { const KeyClass = typeToConstructor(registry, keyType); diff --git a/packages/types/src/codec/BTreeSet.ts b/packages/types/src/codec/BTreeSet.ts index 7602d4feec..f43f6b5c27 100644 --- a/packages/types/src/codec/BTreeSet.ts +++ b/packages/types/src/codec/BTreeSet.ts @@ -15,7 +15,7 @@ import { compareSet, decodeU8a, typeToConstructor } from './utils'; export default class BTreeSet extends Set implements Codec { public readonly registry: Registry; - protected _ValClass: Constructor; + private readonly _ValClass: Constructor; constructor (registry: Registry, valType: Constructor | InterfaceTypes, rawValue: any) { const ValClass = typeToConstructor(registry, valType); diff --git a/packages/types/src/codec/Base.ts b/packages/types/src/codec/Base.ts index 643f4cf01f..a09620c575 100644 --- a/packages/types/src/codec/Base.ts +++ b/packages/types/src/codec/Base.ts @@ -16,7 +16,7 @@ import Raw from './Raw'; export default abstract class Base implements Codec { public readonly registry: Registry; - protected raw: T; + protected readonly raw: T; protected constructor (registry: Registry, value?: any) { this.registry = registry; diff --git a/packages/types/src/codec/Date.ts b/packages/types/src/codec/Date.ts index adc265c080..1d6d6ab06a 100644 --- a/packages/types/src/codec/Date.ts +++ b/packages/types/src/codec/Date.ts @@ -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 */ diff --git a/packages/types/src/codec/Int.ts b/packages/types/src/codec/Int.ts index 7667377e4a..b54cad411c 100644 --- a/packages/types/src/codec/Int.ts +++ b/packages/types/src/codec/Int.ts @@ -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 }); diff --git a/packages/types/src/codec/UInt.ts b/packages/types/src/codec/UInt.ts index 0598b185b0..64ea442d1e 100644 --- a/packages/types/src/codec/UInt.ts +++ b/packages/types/src/codec/UInt.ts @@ -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 }); diff --git a/packages/types/src/create/registry.ts b/packages/types/src/create/registry.ts index 2d2c8925b6..afb84755ac 100644 --- a/packages/types/src/create/registry.ts +++ b/packages/types/src/create/registry.ts @@ -77,21 +77,21 @@ function decorateExtrinsics (registry: Registry, metadata: RegistryMetadata, met } export class TypeRegistry implements Registry { - private _chainProperties?: ChainProperties; + readonly #classes: Map = new Map(); - private _classes: Map = new Map(); + readonly #definitions: Map = new Map(); - private _definitions: Map = new Map(); + readonly #metadataCalls: Record = {}; - private _metadataCalls: Record = {}; + readonly #metadataErrors: Record = {}; - private _metadataErrors: Record = {}; + readonly #metadataEvents: Record> = {}; - private _metadataEvents: Record> = {}; + readonly #unknownTypes: Map = new Map(); - private _metadataExtensions: string[] = defaultExtensions; + #chainProperties?: ChainProperties; - private _unknownTypes: Map = 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 { 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 (name: string, withUnknown?: boolean): Constructor | 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 | 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 (name: string, msg?: string): Constructor { @@ -214,23 +214,23 @@ export class TypeRegistry implements Registry { } public getSignedExtensionExtra (): Record { - return expandExtensionTypes(this._metadataExtensions, 'extra'); + return expandExtensionTypes(this.#metadataExtensions, 'extra'); } public getSignedExtensionTypes (): Record { - 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`); diff --git a/packages/types/src/generic/Event.ts b/packages/types/src/generic/Event.ts index 1b0b96b259..7d987e4c0b 100644 --- a/packages/types/src/generic/Event.ts +++ b/packages/types/src/generic/Event.ts @@ -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; } } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index d50cea8feb..a0a47ffcf3 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -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); diff --git a/packages/types/src/primitive/Text.ts b/packages/types/src/primitive/Text.ts index 197eb9b7d4..8e47008dc8 100644 --- a/packages/types/src/primitive/Text.ts +++ b/packages/types/src/primitive/Text.ts @@ -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(); } /** diff --git a/yarn.lock b/yarn.lock index 97b9766c97..5c0d500d98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"