Some minor cleanups (#1966)

* Some minor cleanups

* Small additions

* Cleanup ExtrinsicEra decoding

* Minor adjustments

* Remove support for metadata v0 parsing

* Adjust refCountDelay

* refCountDelay comments

* Adjust Metadata tests

* Metadata default creation

* Funky lines

* Cleanup as AccountIdAndIndex

* Some derive returns

* Try and cleanup info extraction
This commit is contained in:
Jaco Greeff
2020-02-27 15:01:20 +01:00
committed by GitHub
parent efd6c5331d
commit 4e756704ea
28 changed files with 194 additions and 247 deletions
+5 -4
View File
@@ -47,10 +47,11 @@ export default class Code<ApiType extends ApiTypes> extends BaseWithTx<ApiType>
public createBlueprint = (maxGas: number | BN): CodePutCode<ApiType> => {
return {
signAndSend: this.decorateMethod(
(account: IKeyringPair | string | AccountId | Address): CodePutCodeResultSubscription<ApiType> => this.apiContracts
.putCode(maxGas, compactAddLength(this.code))
.signAndSend(account)
.pipe(map(this.createResult))
(account: IKeyringPair | string | AccountId | Address): CodePutCodeResultSubscription<ApiType> =>
this.apiContracts
.putCode(maxGas, compactAddLength(this.code))
.signAndSend(account)
.pipe(map(this.createResult))
)
};
}
+8 -13
View File
@@ -41,8 +41,8 @@ export default class Contract<ApiType extends ApiTypes> 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<ApiType extends ApiTypes> 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)
)
};
}
+8 -8
View File
@@ -29,14 +29,13 @@ export abstract class Base<ApiType extends ApiTypes> implements ContractBase<Api
}
public get messages (): ContractMessage[] {
return this.abi.abi.contract.messages
.map(
(def: ContractABIMessage, index): ContractMessage => ({
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<ApiType extends ApiTypes> implements ContractBase<Api
: this.abi.abi.contract.messages.findIndex((message): boolean => 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)];
@@ -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]);
}
}
+2 -2
View File
@@ -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<BlockNumber> {
return memo((): Observable<BlockNumber> =>
api.derive.chain.subscribeNewHeads().pipe(
map((header: Header): BlockNumber => header.number.unwrap())
map((header) => header.number.unwrap())
));
}
@@ -26,6 +26,6 @@ import { memo } from '../util';
export function bestNumberFinalized (api: ApiInterfaceRx): () => Observable<BlockNumber> {
return memo((): Observable<BlockNumber> =>
api.rpc.chain.subscribeFinalizedHeads().pipe(
map((header): BlockNumber => header.number.unwrap())
map((header) => header.number.unwrap())
));
}
+1 -4
View File
@@ -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<Vec<AccountId>>
? api.query.session.validators.at(hash)
: of([])
]).pipe(
map(([header, validators]): HeaderExtended =>
+15 -13
View File
@@ -12,6 +12,20 @@ import { Option } from '@polkadot/types';
type Result = [Hash[], Option<Proposal>[], Option<Votes>[]];
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<DerivedCollectiveProposal[]> {
return (): Observable<DerivedCollectiveProposal[]> =>
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[]);
}
@@ -44,7 +44,7 @@ function constructInfo (api: ApiInterfaceRx, index: BN | number, _info: Option<R
export function retrieveInfo (api: ApiInterfaceRx, index: BN | number, info: Option<ReferendumInfo>): Observable<DerivedReferendum | null> {
return ((
info?.isSome
? api.query.democracy.preimages<PreImage>(info.unwrap().proposalHash)
? api.query.democracy.preimages(info.unwrap().proposalHash)
: of(undefined)
) as Observable<PreImage | undefined>).pipe(
map((preimage?: PreImage): DerivedReferendum | null =>
@@ -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)
)
));
}
@@ -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<Deriv
combineLatest([
of(validators),
api.query.imOnline.receivedHeartbeats.multi<Option<Bytes>>(
validators.map((_address, index): [SessionIndex, number] => [currentIndex, index])),
validators.map((_address, index) => [currentIndex, index])),
api.query.imOnline.authoredBlocks.multi<u32>(
validators.map((address): [SessionIndex, AccountId] => [currentIndex, address]))
validators.map((address) => [currentIndex, address]))
])
),
map(([validators, heartbeats, numBlocks]): DerivedHeartbeats =>
+1 -2
View File
@@ -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<BlockNumber> {
return memo((): Observable<BlockNumber> =>
api.derive.session.info().pipe(
map(({ eraLength }: DerivedSessionInfo): BlockNumber => eraLength)
map((info) => info.eraLength)
));
}
@@ -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<BlockNumber> {
return memo((): Observable<BlockNumber> =>
api.derive.session.info().pipe(
map(({ eraProgress }: DerivedSessionInfo): BlockNumber => eraProgress)
map((info) => info.eraProgress)
));
}
@@ -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<BlockNumber> {
return memo((): Observable<BlockNumber> =>
api.derive.session.info().pipe(
map(({ sessionProgress }: DerivedSessionInfo): BlockNumber => sessionProgress)
map((info) => info.sessionProgress)
));
}
+2 -2
View File
@@ -163,14 +163,14 @@ export default class ApiRx extends ApiBase<'rxjs'> {
constructor (options?: ApiOptions) {
super(options, 'rxjs', decorateMethod);
this.#isReadyRx = from(
this.#isReadyRx = from<Promise<ApiRx>>(
// You can create an observable from an event, however my mind groks this form better
new Promise((resolve): void => {
super.on('ready', (): void => {
resolve(this);
});
})
) as Observable<ApiRx>;
);
}
/**
@@ -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()}`);
}
}
}
+9 -21
View File
@@ -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;
}
}
/**
+17 -21
View File
@@ -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();
});
});
});
});
}
@@ -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);
});
+13 -17
View File
@@ -8,35 +8,31 @@ const DELAY = 1750;
/** @internal */
function refCountDelayInner <T> (source: Observable<T>): Observable<T> {
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<T>).connect();
connected = 3;
connection = (source as ConnectableObservable<T>).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);
}
}
@@ -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/);
});
+10 -6
View File
@@ -17,18 +17,22 @@ function notEntry (value: any): boolean {
return !Array.isArray(value) || value.length !== 2;
}
function compareMapArray (a: Map<any, any>, 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<string, Codec> that the first has to be as well
export default function compareMap (a: Map<any, any>, 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;
+8 -4
View File
@@ -4,16 +4,20 @@
import { isObject } from '@polkadot/util';
function compareSetArray (a: Set<any>, 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<string, Codec> that the first has to be as well
export default function compareSet (a: Set<any>, 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;
+61 -59
View File
@@ -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<ExtrinsicVx | ExtrinsicUnknown> 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<ExtrinsicVx | ExtrinsicUnknown> {
/**
* @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<ExtrinsicVx | ExtrinsicUnknown> 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<ExtrinsicVx | ExtrinsicUnknown> 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
+11 -17
View File
@@ -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;
}
/**
+4
View File
@@ -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);
+1 -3
View File
@@ -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);
@@ -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 => {