diff --git a/packages/api-contract/src/base/Code.ts b/packages/api-contract/src/base/Code.ts index 7c40054450..6029afdab0 100644 --- a/packages/api-contract/src/base/Code.ts +++ b/packages/api-contract/src/base/Code.ts @@ -47,10 +47,11 @@ export default class Code extends BaseWithTx public createBlueprint = (maxGas: number | BN): CodePutCode => { return { signAndSend: this.decorateMethod( - (account: IKeyringPair | string | AccountId | Address): CodePutCodeResultSubscription => this.apiContracts - .putCode(maxGas, compactAddLength(this.code)) - .signAndSend(account) - .pipe(map(this.createResult)) + (account: IKeyringPair | string | AccountId | Address): CodePutCodeResultSubscription => + this.apiContracts + .putCode(maxGas, compactAddLength(this.code)) + .signAndSend(account) + .pipe(map(this.createResult)) ) }; } diff --git a/packages/api-contract/src/base/Contract.ts b/packages/api-contract/src/base/Contract.ts index 95d9d3b1f6..b51d037256 100644 --- a/packages/api-contract/src/base/Contract.ts +++ b/packages/api-contract/src/base/Contract.ts @@ -41,8 +41,8 @@ export default class Contract extends BaseWithTxAndRpc return { send: this.decorateMethod( as === 'rpc' && this.hasRpcContractsCall - ? (account: IKeyringPair | string | AccountId | Address): ContractCallResult<'rpc'> => { - return this.rpcContractsCall( + ? (account: IKeyringPair | string | AccountId | Address): ContractCallResult<'rpc'> => + this.rpcContractsCall( this.registry.createType('ContractCallRequest', { origin: account, dest: this.address.toString(), @@ -50,18 +50,13 @@ export default class Contract extends BaseWithTxAndRpc gasLimit, inputData: fn(...params) }) - ) - .pipe( - map((result: ContractExecResult): ContractCallOutcome => - this.createOutcome(result, this.registry.createType('AccountId', account), def, params) - ) - ); - } - : (account: IKeyringPair | string | AccountId | Address): ContractCallResult<'tx'> => { - return this.apiContracts + ).pipe(map((result: ContractExecResult): ContractCallOutcome => + this.createOutcome(result, this.registry.createType('AccountId', account), def, params) + )) + : (account: IKeyringPair | string | AccountId | Address): ContractCallResult<'tx'> => + this.apiContracts .call(this.address, value, gasLimit, fn(...params)) - .signAndSend(account); - } + .signAndSend(account) ) }; } diff --git a/packages/api-contract/src/base/util.ts b/packages/api-contract/src/base/util.ts index a70442e0e4..ad7dcb3613 100644 --- a/packages/api-contract/src/base/util.ts +++ b/packages/api-contract/src/base/util.ts @@ -29,14 +29,13 @@ export abstract class Base implements ContractBase ({ - index, - def, - fn: this.abi.messages[def.name] || this.abi.messages[stringCamelCase(name)] - }) - ); + return this.abi.abi.contract.messages.map( + (def: ContractABIMessage, index): ContractMessage => ({ + index, + def, + fn: this.abi.messages[def.name] || this.abi.messages[stringCamelCase(name)] + }) + ); } public getMessage (nameOrIndex?: string | number): ContractMessage { @@ -46,6 +45,7 @@ export abstract class Base implements ContractBase nameOrIndex === message.name || nameOrIndex === stringCamelCase(message.name)) : 0; const def = this.abi.abi.contract.messages[index]; + assert(!!def, `Attempted to access a contract message that does not exist: ${typeof nameOrIndex === 'number' ? `index ${nameOrIndex}` : nameOrIndex}`); const fn = this.abi.messages[def.name] || this.abi.messages[stringCamelCase(def.name)]; diff --git a/packages/api-derive/src/accounts/idAndIndex.ts b/packages/api-derive/src/accounts/idAndIndex.ts index 4078e11f5c..2722c22550 100644 --- a/packages/api-derive/src/accounts/idAndIndex.ts +++ b/packages/api-derive/src/accounts/idAndIndex.ts @@ -26,17 +26,17 @@ function retrieve (api: ApiInterfaceRx, address: Address | AccountId | AccountIn const accountId = createType(api.registry, 'AccountId', decoded); return api.derive.accounts.idToIndex(accountId).pipe( - map((accountIndex): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex) + map((accountIndex): AccountIdAndIndex => [accountId, accountIndex]) ); } const accountIndex = createType(api.registry, 'AccountIndex', decoded); return api.derive.accounts.indexToId(accountIndex).pipe( - map((accountId): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex) + map((accountId): AccountIdAndIndex => [accountId, accountIndex]) ); } catch (error) { - return of([undefined, undefined] as AccountIdAndIndex); + return of([undefined, undefined]); } } diff --git a/packages/api-derive/src/chain/bestNumber.ts b/packages/api-derive/src/chain/bestNumber.ts index ee3c3cefbd..6442a1695e 100644 --- a/packages/api-derive/src/chain/bestNumber.ts +++ b/packages/api-derive/src/chain/bestNumber.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { BlockNumber, Header } from '@polkadot/types/interfaces'; +import { BlockNumber } from '@polkadot/types/interfaces'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -25,6 +25,6 @@ import { memo } from '../util'; export function bestNumber (api: ApiInterfaceRx): () => Observable { return memo((): Observable => api.derive.chain.subscribeNewHeads().pipe( - map((header: Header): BlockNumber => header.number.unwrap()) + map((header) => header.number.unwrap()) )); } diff --git a/packages/api-derive/src/chain/bestNumberFinalized.ts b/packages/api-derive/src/chain/bestNumberFinalized.ts index 24e783ab2c..d0f17dc742 100644 --- a/packages/api-derive/src/chain/bestNumberFinalized.ts +++ b/packages/api-derive/src/chain/bestNumberFinalized.ts @@ -26,6 +26,6 @@ import { memo } from '../util'; export function bestNumberFinalized (api: ApiInterfaceRx): () => Observable { return memo((): Observable => api.rpc.chain.subscribeFinalizedHeads().pipe( - map((header): BlockNumber => header.number.unwrap()) + map((header) => header.number.unwrap()) )); } diff --git a/packages/api-derive/src/chain/getHeader.ts b/packages/api-derive/src/chain/getHeader.ts index 1d2124d18f..7f11cb2779 100644 --- a/packages/api-derive/src/chain/getHeader.ts +++ b/packages/api-derive/src/chain/getHeader.ts @@ -2,14 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AccountId } from '@polkadot/types/interfaces'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { Observable, combineLatest, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; -import { Vec } from '@polkadot/types'; - import { HeaderExtended } from '../type'; import { memo } from '../util'; @@ -32,7 +29,7 @@ export function getHeader (api: ApiInterfaceRx): (hash: Uint8Array | string) => combineLatest([ api.rpc.chain.getHeader(hash), api.query.session - ? api.query.session.validators.at(hash) as Observable> + ? api.query.session.validators.at(hash) : of([]) ]).pipe( map(([header, validators]): HeaderExtended => diff --git a/packages/api-derive/src/collective/proposals.ts b/packages/api-derive/src/collective/proposals.ts index 3728941df7..2dfaf784d2 100644 --- a/packages/api-derive/src/collective/proposals.ts +++ b/packages/api-derive/src/collective/proposals.ts @@ -12,6 +12,20 @@ import { Option } from '@polkadot/types'; type Result = [Hash[], Option[], Option[]]; +function parse ([hashes, proposals, votes]: Result): DerivedCollectiveProposal[] { + return proposals + .map((proposalOpt, index): DerivedCollectiveProposal | null => + proposalOpt.isSome + ? { + hash: hashes[index], + proposal: proposalOpt.unwrap(), + votes: votes[index].unwrapOr(null) + } + : null + ) + .filter((proposal): proposal is DerivedCollectiveProposal => !!proposal); +} + export function proposals (api: ApiInterfaceRx, section: 'council' | 'technicalCommittee'): () => Observable { return (): Observable => api.query[section] @@ -25,19 +39,7 @@ export function proposals (api: ApiInterfaceRx, section: 'council' | 'technicalC ]) : of([[], [], []]) ), - map(([hashes, proposals, votes]: Result): DerivedCollectiveProposal[] => - proposals - .map((proposalOpt, index): DerivedCollectiveProposal | null => - proposalOpt.isSome - ? { - hash: hashes[index], - proposal: proposalOpt.unwrap(), - votes: votes[index].unwrapOr(null) - } - : null - ) - .filter((proposal): boolean => !!proposal) as DerivedCollectiveProposal[] - ) + map(parse) ) : of([] as DerivedCollectiveProposal[]); } diff --git a/packages/api-derive/src/democracy/referendumInfo.ts b/packages/api-derive/src/democracy/referendumInfo.ts index 20cf74d99d..33c4532706 100644 --- a/packages/api-derive/src/democracy/referendumInfo.ts +++ b/packages/api-derive/src/democracy/referendumInfo.ts @@ -44,7 +44,7 @@ function constructInfo (api: ApiInterfaceRx, index: BN | number, _info: Option): Observable { return (( info?.isSome - ? api.query.democracy.preimages(info.unwrap().proposalHash) + ? api.query.democracy.preimages(info.unwrap().proposalHash) : of(undefined) ) as Observable).pipe( map((preimage?: PreImage): DerivedReferendum | null => diff --git a/packages/api-derive/src/democracy/referendumInfos.ts b/packages/api-derive/src/democracy/referendumInfos.ts index 5b835aeee9..4d7920939f 100644 --- a/packages/api-derive/src/democracy/referendumInfos.ts +++ b/packages/api-derive/src/democracy/referendumInfos.ts @@ -29,7 +29,7 @@ export function referendumInfos (api: ApiInterfaceRx): (ids?: (BN | number)[]) = ) ), map((infos): DerivedReferendum[] => - infos.filter((referendum): boolean => !!referendum) as DerivedReferendum[] + infos.filter((referendum): referendum is DerivedReferendum => !!referendum) ) )); } diff --git a/packages/api-derive/src/imOnline/receivedHeartbeats.ts b/packages/api-derive/src/imOnline/receivedHeartbeats.ts index 3be9fc7d6d..d73a0b30d0 100644 --- a/packages/api-derive/src/imOnline/receivedHeartbeats.ts +++ b/packages/api-derive/src/imOnline/receivedHeartbeats.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AccountId, SessionIndex } from '@polkadot/types/interfaces'; +import { AccountId } from '@polkadot/types/interfaces'; import { DerivedHeartbeats } from '../types'; import { of, Observable, combineLatest } from 'rxjs'; @@ -23,9 +23,9 @@ export function receivedHeartbeats (api: ApiInterfaceRx): () => Observable>( - validators.map((_address, index): [SessionIndex, number] => [currentIndex, index])), + validators.map((_address, index) => [currentIndex, index])), api.query.imOnline.authoredBlocks.multi( - validators.map((address): [SessionIndex, AccountId] => [currentIndex, address])) + validators.map((address) => [currentIndex, address])) ]) ), map(([validators, heartbeats, numBlocks]): DerivedHeartbeats => diff --git a/packages/api-derive/src/session/eraLength.ts b/packages/api-derive/src/session/eraLength.ts index 091767c0ca..b1350ec826 100644 --- a/packages/api-derive/src/session/eraLength.ts +++ b/packages/api-derive/src/session/eraLength.ts @@ -3,7 +3,6 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { BlockNumber } from '@polkadot/types/interfaces'; -import { DerivedSessionInfo } from '../types'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -14,6 +13,6 @@ import { memo } from '../util'; export function eraLength (api: ApiInterfaceRx): () => Observable { return memo((): Observable => api.derive.session.info().pipe( - map(({ eraLength }: DerivedSessionInfo): BlockNumber => eraLength) + map((info) => info.eraLength) )); } diff --git a/packages/api-derive/src/session/eraProgress.ts b/packages/api-derive/src/session/eraProgress.ts index 522070eb06..01a2675a3c 100644 --- a/packages/api-derive/src/session/eraProgress.ts +++ b/packages/api-derive/src/session/eraProgress.ts @@ -3,7 +3,6 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { BlockNumber } from '@polkadot/types/interfaces'; -import { DerivedSessionInfo } from '../types'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -14,6 +13,6 @@ import { memo } from '../util'; export function eraProgress (api: ApiInterfaceRx): () => Observable { return memo((): Observable => api.derive.session.info().pipe( - map(({ eraProgress }: DerivedSessionInfo): BlockNumber => eraProgress) + map((info) => info.eraProgress) )); } diff --git a/packages/api-derive/src/session/sessionProgress.ts b/packages/api-derive/src/session/sessionProgress.ts index a85cb70f92..3205077c8f 100644 --- a/packages/api-derive/src/session/sessionProgress.ts +++ b/packages/api-derive/src/session/sessionProgress.ts @@ -3,7 +3,6 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { BlockNumber } from '@polkadot/types/interfaces'; -import { DerivedSessionInfo } from '../types'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -14,6 +13,6 @@ import { memo } from '../util'; export function sessionProgress (api: ApiInterfaceRx): () => Observable { return memo((): Observable => api.derive.session.info().pipe( - map(({ sessionProgress }: DerivedSessionInfo): BlockNumber => sessionProgress) + map((info) => info.sessionProgress) )); } diff --git a/packages/api/src/rx/Api.ts b/packages/api/src/rx/Api.ts index 933c5073ef..5ec35ae1aa 100644 --- a/packages/api/src/rx/Api.ts +++ b/packages/api/src/rx/Api.ts @@ -163,14 +163,14 @@ 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 => { resolve(this); }); }) - ) as Observable; + ); } /** diff --git a/packages/metadata/src/Metadata/MagicNumber.ts b/packages/metadata/src/Metadata/MagicNumber.ts index 9c6f32d864..382da3adaa 100644 --- a/packages/metadata/src/Metadata/MagicNumber.ts +++ b/packages/metadata/src/Metadata/MagicNumber.ts @@ -14,8 +14,10 @@ export default class MagicNumber extends U32 { constructor (registry: Registry, value?: any) { super(registry, value); - const magic = new U32(registry, MAGIC_NUMBER); + if (!this.isEmpty) { + const magic = new U32(registry, MAGIC_NUMBER); - assert(this.eq(magic), `MagicNumber mismatch: expected ${magic.toHex()}, found ${this.toHex()}`); + assert(this.eq(magic), `MagicNumber mismatch: expected ${magic.toHex()}, found ${this.toHex()}`); + } } } diff --git a/packages/metadata/src/Metadata/Metadata.ts b/packages/metadata/src/Metadata/Metadata.ts index 0a7e9d4ccb..d332eecab0 100644 --- a/packages/metadata/src/Metadata/Metadata.ts +++ b/packages/metadata/src/Metadata/Metadata.ts @@ -4,9 +4,8 @@ import { Registry } from '@polkadot/types/types'; -import { bnToU8a, isHex, hexToU8a, u8aConcat } from '@polkadot/util'; +import { isHex, hexToU8a } from '@polkadot/util'; -import { MAGIC_NUMBER } from './MagicNumber'; import MetadataVersioned from './MetadataVersioned'; const VERSION_IDX = 4; // magic u32 preceding @@ -23,28 +22,17 @@ function decodeMetadata (registry: Registry, _value: Uint8Array | string = new U try { return new MetadataVersioned(registry, value); } catch (error) { - if (!error.message.includes('MagicNumber mismatch')) { - // This is an f-ing hack as a follow-up to another ugly hack - // https://github.com/polkadot-js/api/commit/a9211690be6b68ad6c6dad7852f1665cadcfa5b2 - // when we fail on V9, try to re-parse it as v10... yes... HACK - if (version === 9) { - value[VERSION_IDX] = 10; + // This is an f-ing hack as a follow-up to another ugly hack + // https://github.com/polkadot-js/api/commit/a9211690be6b68ad6c6dad7852f1665cadcfa5b2 + // when we fail on V9, try to re-parse it as v10... yes... HACK + if (version === 9) { + value[VERSION_IDX] = 10; - return decodeMetadata(registry, value); - } - - throw error; + return decodeMetadata(registry, value); } - } - return new MetadataVersioned( - registry, - u8aConcat( - bnToU8a(MAGIC_NUMBER), // manually add the magic number - Uint8Array.from([0]), // add the version for the original - value // the actual data as retrieved - ) - ); + throw error; + } } /** diff --git a/packages/metadata/src/Metadata/util/testUtil.ts b/packages/metadata/src/Metadata/util/testUtil.ts index 8324ff678f..0f6f302c2d 100644 --- a/packages/metadata/src/Metadata/util/testUtil.ts +++ b/packages/metadata/src/Metadata/util/testUtil.ts @@ -47,31 +47,27 @@ export function defaultValues (registry: Registry, rpcData: string, withThrow = describe('storage with default values', (): void => { const metadata = new Metadata(registry, rpcData); - metadata.asLatest.modules - .filter(({ storage }): boolean => storage.isSome) - .forEach((mod): void => { - mod.storage.unwrap().items.forEach(({ fallback, name, type }): void => { - const inner = unwrapStorageType(type); - const location = `${mod.name}.${name}: ${inner}`; + metadata.asLatest.modules.filter(({ storage }): boolean => storage.isSome).forEach((mod): void => { + mod.storage.unwrap().items.forEach(({ fallback, name, type }): void => { + const inner = unwrapStorageType(type); + const location = `${mod.name}.${name}: ${inner}`; - it(`creates default types for ${location}`, (): void => { - expect( - (): void => { - try { - createTypeUnsafe(registry, inner, [fallback]); - } catch (error) { - const message = `${location}:: ${error.message}`; + it(`creates default types for ${location}`, (): void => { + expect((): void => { + try { + createTypeUnsafe(registry, inner, [fallback]); + } catch (error) { + const message = `${location}:: ${error.message}`; - if (withThrow) { - throw new Error(message); - } else { - console.warn(message); - } - } + if (withThrow) { + throw new Error(message); + } else { + console.warn(message); } - ).not.toThrow(); - }); + } + }).not.toThrow(); }); }); + }); }); } diff --git a/packages/metadata/src/Metadata/v0/Metadata.spec.ts b/packages/metadata/src/Metadata/v0/Metadata.spec.ts deleted file mode 100644 index a2d158f391..0000000000 --- a/packages/metadata/src/Metadata/v0/Metadata.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017-2020 @polkadot/metadata 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 { TypeRegistry } from '@polkadot/types/create'; - -import Metadata from '../'; -import latestSubstrateV0 from './static-substrate.json'; -import rpcData from './static'; -import { defaultValues, toLatest } from '../util/testUtil'; - -describe('Metadata', (): void => { - const registry = new TypeRegistry(); - - it('decodes into latest substrate', (): void => { - const metadata = new Metadata(registry, rpcData); - const json = metadata.asV0.toJSON(); - - try { - expect(metadata.version).toBe(0); - expect(metadata.asV0.modules.length).not.toBe(0); - expect(json).toEqual(latestSubstrateV0); - } catch (error) { - console.error(JSON.stringify(json)); - - throw error; - } - }); - - toLatest(registry, 0, rpcData, false); - - defaultValues(registry, rpcData, false); -}); diff --git a/packages/rpc-core/src/rxjs/refCountDelay.ts b/packages/rpc-core/src/rxjs/refCountDelay.ts index 339557669f..7b8f5c69ca 100644 --- a/packages/rpc-core/src/rxjs/refCountDelay.ts +++ b/packages/rpc-core/src/rxjs/refCountDelay.ts @@ -8,35 +8,31 @@ const DELAY = 1750; /** @internal */ function refCountDelayInner (source: Observable): Observable { - let connected = 0; // 0 = disconnected, 1 = disconnecting, 2 = connecting, 3 = connected - let refCount = 0; - let con = Subscription.EMPTY; - let sched = Subscription.EMPTY; + // state: 0 = disconnected, 1 = disconnecting, 2 = connecting, 3 = connected + let [state, refCount, connection, scheduler] = [0, 0, Subscription.EMPTY, Subscription.EMPTY]; return new Observable((ob) => { source.subscribe(ob); if (refCount++ === 0) { - if (connected === 1) { - connected = 3; - sched.unsubscribe(); + if (state === 1) { + scheduler.unsubscribe(); } else { - con = (source as ConnectableObservable).connect(); - connected = 3; + connection = (source as ConnectableObservable).connect(); } + + state = 3; } return (): void => { if (--refCount === 0) { - if (connected === 2) { - connected = 0; - sched.unsubscribe(); + if (state === 2) { + state = 0; scheduler.unsubscribe(); } else { - // connected === 3 - connected = 1; - sched = asapScheduler.schedule((): void => { - con.unsubscribe(); - connected = 0; + // state === 3 + state = 1; + scheduler = asapScheduler.schedule((): void => { + state = 0; connection.unsubscribe(); }, DELAY); } } diff --git a/packages/rpc-provider/src/coder/decodeResponse.spec.ts b/packages/rpc-provider/src/coder/decodeResponse.spec.ts index d344c5bd7d..47bb7399c3 100644 --- a/packages/rpc-provider/src/coder/decodeResponse.spec.ts +++ b/packages/rpc-provider/src/coder/decodeResponse.spec.ts @@ -14,7 +14,7 @@ describe('decodeResponse', (): void => { it('expects a non-empty input object', (): void => { expect( - (): any => coder.decodeResponse((undefined as any) as JsonRpcResponse) + (): any => coder.decodeResponse(undefined as unknown as JsonRpcResponse) ).toThrow(/Empty response/); }); diff --git a/packages/types/src/codec/utils/compareMap.ts b/packages/types/src/codec/utils/compareMap.ts index 6f86dc3486..4565a37a58 100644 --- a/packages/types/src/codec/utils/compareMap.ts +++ b/packages/types/src/codec/utils/compareMap.ts @@ -17,18 +17,22 @@ function notEntry (value: any): boolean { return !Array.isArray(value) || value.length !== 2; } +function compareMapArray (a: Map, b: [any, any][]): boolean { + // equal number of entries and each entry in the array should match + return (a.size === b.length) && !b.some((entry): boolean => + notEntry(entry) || hasMismatch(a.get(entry[0]), entry[1]) + ); +} + // NOTE These are used internally and when comparing objects, expects that // when the second is an Map that the first has to be as well export default function compareMap (a: Map, b?: any): boolean { if (Array.isArray(b)) { - // equal number of entries and each entry in the array should match - return (a.size === b.length) && !b.some((entry): boolean => - notEntry(entry) || hasMismatch(a.get(entry[0]), entry[1]) - ); + return compareMapArray(a, b); } else if (b instanceof Map) { - return compareMap(a, [...b.entries()]); + return compareMapArray(a, [...b.entries()]); } else if (isObject(b)) { - return compareMap(a, Object.entries(b)); + return compareMapArray(a, Object.entries(b)); } return false; diff --git a/packages/types/src/codec/utils/compareSet.ts b/packages/types/src/codec/utils/compareSet.ts index 11a96adc29..b8039d1de9 100644 --- a/packages/types/src/codec/utils/compareSet.ts +++ b/packages/types/src/codec/utils/compareSet.ts @@ -4,16 +4,20 @@ import { isObject } from '@polkadot/util'; +function compareSetArray (a: Set, b: any[]): boolean { + // equal number of entries and each entry in the array should match + return (a.size === b.length) && !b.some((entry): boolean => !a.has(entry)); +} + // NOTE These are used internally and when comparing objects, expects that // when the second is an Set that the first has to be as well export default function compareSet (a: Set, b?: any): boolean { if (Array.isArray(b)) { - // equal number of entries and each entry in the array should match - return (a.size === b.length) && !b.some((entry): boolean => !a.has(entry)); + return compareSetArray(a, b); } else if (b instanceof Set) { - return compareSet(a, [...b.entries()]); + return compareSetArray(a, [...b.entries()]); } else if (isObject(b)) { - return compareSet(a, Object.entries(b)); + return compareSetArray(a, Object.entries(b)); } return false; diff --git a/packages/types/src/extrinsic/Extrinsic.ts b/packages/types/src/extrinsic/Extrinsic.ts index 6edc15d155..8114cc0e95 100644 --- a/packages/types/src/extrinsic/Extrinsic.ts +++ b/packages/types/src/extrinsic/Extrinsic.ts @@ -38,64 +38,7 @@ const VERSIONS: (keyof InterfaceTypes)[] = [ export { TRANSACTION_VERSION as LATEST_EXTRINSIC_VERSION } from './v4/Extrinsic'; -/** - * @name GenericExtrinsic - * @description - * Representation of an Extrinsic in the system. It contains the actual call, - * (optional) signature and encodes with an actual length prefix - * - * {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}. - * - * Can be: - * - signed, to create a transaction - * - left as is, to create an inherent - */ -export default class Extrinsic extends Base implements IExtrinsic { - constructor (registry: Registry, value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, { version }: CreateOptions = {}) { - super(registry, Extrinsic.decodeExtrinsic(registry, value, version)); - } - - /** @internal */ - private static newFromValue (registry: Registry, value: any, version: number): ExtrinsicVx | ExtrinsicUnknown { - if (value instanceof Extrinsic) { - return value.raw; - } - - const isSigned = (version & BIT_SIGNED) === BIT_SIGNED; - const type = VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; - - // we cast here since the VERSION definition is incredibly broad - we don't have a - // slice for "only add extrinsic types", and more string definitions become unwieldy - return registry.createType(type, value, { isSigned, version }) as ExtrinsicVx; - } - - /** @internal */ - public static decodeExtrinsic (registry: Registry, value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, version: number = DEFAULT_VERSION): ExtrinsicVx | ExtrinsicUnknown { - if (isU8a(value) || Array.isArray(value) || isHex(value)) { - return Extrinsic.decodeU8a(registry, u8aToU8a(value), version); - } else if (value instanceof registry.createClass('Call')) { - return Extrinsic.newFromValue(registry, { method: value }, version); - } - - return Extrinsic.newFromValue(registry, value, version); - } - - /** @internal */ - private static decodeU8a (registry: Registry, value: Uint8Array, version: number): ExtrinsicVx | ExtrinsicUnknown { - if (!value.length) { - return Extrinsic.newFromValue(registry, new Uint8Array(), version); - } - - const [offset, length] = Compact.decodeU8a(value); - const total = offset + length.toNumber(); - - assert(total <= value.length, `Extrinsic: length less than remainder, expected at least ${total}, found ${value.length}`); - - const data = value.subarray(offset, total); - - return Extrinsic.newFromValue(registry, data.subarray(1), data[0]); - } - +abstract class ExtrinsicBase extends Base { /** * @description The arguments passed to for the call, exposes args so it is compatible with [[Call]] */ @@ -214,6 +157,65 @@ export default class Extrinsic extends Base impl public get version (): number { return this.type | (this.isSigned ? BIT_SIGNED : BIT_UNSIGNED); } +} + +/** + * @name GenericExtrinsic + * @description + * Representation of an Extrinsic in the system. It contains the actual call, + * (optional) signature and encodes with an actual length prefix + * + * {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}. + * + * Can be: + * - signed, to create a transaction + * - left as is, to create an inherent + */ +export default class Extrinsic extends ExtrinsicBase implements IExtrinsic { + constructor (registry: Registry, value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, { version }: CreateOptions = {}) { + super(registry, Extrinsic.decodeExtrinsic(registry, value, version)); + } + + /** @internal */ + private static newFromValue (registry: Registry, value: any, version: number): ExtrinsicVx | ExtrinsicUnknown { + if (value instanceof Extrinsic) { + return value.raw; + } + + const isSigned = (version & BIT_SIGNED) === BIT_SIGNED; + const type = VERSIONS[version & UNMASK_VERSION] || VERSIONS[0]; + + // we cast here since the VERSION definition is incredibly broad - we don't have a + // slice for "only add extrinsic types", and more string definitions become unwieldy + return registry.createType(type, value, { isSigned, version }) as ExtrinsicVx; + } + + /** @internal */ + public static decodeExtrinsic (registry: Registry, value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, version: number = DEFAULT_VERSION): ExtrinsicVx | ExtrinsicUnknown { + if (isU8a(value) || Array.isArray(value) || isHex(value)) { + return Extrinsic.decodeU8a(registry, u8aToU8a(value), version); + } else if (value instanceof registry.createClass('Call')) { + return Extrinsic.newFromValue(registry, { method: value }, version); + } + + return Extrinsic.newFromValue(registry, value, version); + } + + /** @internal */ + private static decodeU8a (registry: Registry, value: Uint8Array, version: number): ExtrinsicVx | ExtrinsicUnknown { + if (!value.length) { + return Extrinsic.newFromValue(registry, new Uint8Array(), version); + } + + const [offset, length] = Compact.decodeU8a(value); + const total = offset + length.toNumber(); + + assert(total <= value.length, `Extrinsic: length less than remainder, expected at least ${total}, found ${value.length}`); + + const data = value.subarray(offset, total); + + return Extrinsic.newFromValue(registry, data.subarray(1), data[0]); + } /** * @description Injects an already-generated signature into the extrinsic @@ -289,7 +291,7 @@ export default class Extrinsic extends Base impl */ public toU8a (isBare?: boolean): Uint8Array { // we do not apply bare to the internal values, rather this only determines out length addition, - // where we strip all lengths this creates an un-decodable extrinsic + // where we strip all lengths this creates an extrinsic that cannot be decoded const encoded = u8aConcat(new Uint8Array([this.version]), this.raw.toU8a()); return isBare diff --git a/packages/types/src/extrinsic/ExtrinsicEra.ts b/packages/types/src/extrinsic/ExtrinsicEra.ts index d51841609d..5c7d8d0734 100644 --- a/packages/types/src/extrinsic/ExtrinsicEra.ts +++ b/packages/types/src/extrinsic/ExtrinsicEra.ts @@ -214,25 +214,21 @@ export default class ExtrinsicEra extends Enum implements IExtrinsicEra { /** @internal */ // eslint-disable-next-line @typescript-eslint/ban-types private static decodeExtrinsicEra (value: IExtrinsicEra | MortalMethod | MortalEnumDef | ImmortalEnumDef | Uint8Array | string = new Uint8Array()): Uint8Array | Object | undefined { - if (!value) { - return new Uint8Array([0]); - } else if (value instanceof ExtrinsicEra) { + if (value instanceof ExtrinsicEra) { return ExtrinsicEra.decodeExtrinsicEra(value.toU8a()); } else if (isHex(value)) { return ExtrinsicEra.decodeExtrinsicEra(hexToU8a(value)); - } else if (isU8a(value)) { - return (!value.length || value[0] === 0) + } else if (!value || isU8a(value)) { + return (!value?.length || value[0] === 0) ? new Uint8Array([0]) : new Uint8Array([1, value[0], value[1]]); } else if (isObject(value)) { // this is to de-serialize from JSON - if ((value as MortalEnumDef).MortalEra) { - return { MortalEra: (value as MortalEnumDef).MortalEra }; - } else if ((value as ImmortalEnumDef).ImmortalEra) { - return { ImmortalEra: (value as ImmortalEnumDef).ImmortalEra }; - } - - return { MortalEra: value }; + return (value as MortalEnumDef).MortalEra + ? { MortalEra: (value as MortalEnumDef).MortalEra } + : (value as ImmortalEnumDef).ImmortalEra + ? { ImmortalEra: (value as ImmortalEnumDef).ImmortalEra } + : { MortalEra: value }; } throw new Error('Invalid data passed to Era'); @@ -242,11 +238,9 @@ export default class ExtrinsicEra extends Enum implements IExtrinsicEra { * @description Override the encoded length method */ public get encodedLength (): number { - if (this.index === 0) { - return this.asImmortalEra.encodedLength; - } else { - return this.asMortalEra.encodedLength; - } + return this.isImmortalEra + ? this.asImmortalEra.encodedLength + : this.asMortalEra.encodedLength; } /** diff --git a/packages/types/src/generic/Vote.spec.ts b/packages/types/src/generic/Vote.spec.ts index 67db1ebbd6..9a226d3a17 100644 --- a/packages/types/src/generic/Vote.spec.ts +++ b/packages/types/src/generic/Vote.spec.ts @@ -21,6 +21,10 @@ describe('Vote', (): void => { expect(new Vote(registry, false).isAye).toBe(false); }); + it('constructs via undefined', (): void => { + expect(new Vote(registry).isNay).toBe(true); + }); + it('has isYay for positive', (): void => { // eslint-disable-next-line no-new-wrappers expect(new Vote(registry, true).isAye).toBe(true); diff --git a/packages/types/src/generic/Vote.ts b/packages/types/src/generic/Vote.ts index 73fc3f5454..3d78bb94b6 100644 --- a/packages/types/src/generic/Vote.ts +++ b/packages/types/src/generic/Vote.ts @@ -41,9 +41,7 @@ function decodeVoteU8a (value: Uint8Array): Uint8Array { /** @internal */ function decodeVote (registry: Registry, value?: InputTypes): Uint8Array { - if (isUndefined(value)) { - return decodeVoteBool(false); - } else if (value instanceof Boolean || isBoolean(value)) { + if (isUndefined(value) || value instanceof Boolean || isBoolean(value)) { return decodeVoteBool(new Bool(registry, value).isTrue); } else if (isNumber(value)) { return decodeVoteBool(value < 0); diff --git a/packages/types/src/interfaces/system/EventRecord.spec.ts b/packages/types/src/interfaces/system/EventRecord.spec.ts index c7efbff878..50186ad4d3 100644 --- a/packages/types/src/interfaces/system/EventRecord.spec.ts +++ b/packages/types/src/interfaces/system/EventRecord.spec.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import Metadata from '@polkadot/metadata/Metadata'; -import rpcMetadataV0 from '@polkadot/metadata/Metadata/v0/static'; +import rpcMetadataV1 from '@polkadot/metadata/Metadata/v1/static'; import rpcMetadata from '@polkadot/metadata/Metadata/static'; import { TypeRegistry } from '../../create'; @@ -16,7 +16,7 @@ describe('EventRecord', (): void => { describe('EventRecordTo76', (): void => { beforeEach((): void => { // eslint-disable-next-line no-new - new Metadata(registry, rpcMetadataV0); + new Metadata(registry, rpcMetadataV1); }); it('decodes correctly', (): void => {