diff --git a/packages/types-codec/src/abstract/Int.ts b/packages/types-codec/src/abstract/Int.ts index 1278c254ea..8f69d58027 100644 --- a/packages/types-codec/src/abstract/Int.ts +++ b/packages/types-codec/src/abstract/Int.ts @@ -4,7 +4,7 @@ import type { HexString } from '@polkadot/util/types'; import type { AnyNumber, Inspect, INumber, IU8a, Registry, UIntBitLength } from '../types'; -import { BN, BN_BILLION, BN_HUNDRED, BN_MILLION, BN_QUINTILL, bnToBn, bnToHex, bnToU8a, formatBalance, formatNumber, hexToBn, isBn, isHex, isNumber, isString, isU8a, u8aToBn, u8aToNumber } from '@polkadot/util'; +import { BN, BN_BILLION, BN_HUNDRED, BN_MILLION, BN_QUINTILL, bnToBn, bnToHex, bnToU8a, formatBalance, formatNumber, hexToBn, isBn, isFunction, isHex, isNumber, isObject, isString, isU8a, u8aToBn, u8aToNumber } from '@polkadot/util'; export const DEFAULT_UINT_BITS = 64; @@ -25,7 +25,7 @@ function toPercentage (value: BN, divisor: BN): string { } /** @internal */ -function decodeAbstractInt (value: Exclude, isNegative: boolean): string | number { +function decodeAbstractInt (value: Exclude | Record, isNegative: boolean): string | number { if (isNumber(value)) { if (!Number.isInteger(value) || value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) { throw new Error('Number needs to be an integer <= Number.MAX_SAFE_INTEGER, i.e. 2 ^ 53 - 1'); @@ -44,9 +44,26 @@ function decodeAbstractInt (value: Exclude, isNegative: b return value; } else if (isBn(value)) { return value.toString(); + } else if (isObject(value) && !isFunction(value.toBn)) { + // Allow the construction from an object with a single top-level key. This means that + // single key objects can be treated equivalently to numbers, assuming they meet the + // specific requirements. (This is useful in Weights 1.5 where Objects are compact) + const keys = Object.keys(value); + + if (keys.length !== 1) { + throw new Error('Unable to construct number from multi-key object'); + } + + const inner = value[keys[0]]; + + if (!isString(inner) && !isNumber(inner)) { + throw new Error('Unable to construct from object with non-string/non-number value'); + } + + return decodeAbstractInt(inner, isNegative); } - return bnToBn(value).toString(); + return bnToBn(value as bigint).toString(); } /** diff --git a/packages/types-codec/src/base/Int.spec.ts b/packages/types-codec/src/base/Int.spec.ts index 67feafc167..57c07701ed 100644 --- a/packages/types-codec/src/base/Int.spec.ts +++ b/packages/types-codec/src/base/Int.spec.ts @@ -25,6 +25,17 @@ describe('Int', (): void => { ).toEqual(-1234); }); + it('can construct via a single-entry struct', (): void => { + expect( + // @ts-expect-error We could receive these via JSON + new Int(registry, { ref_time: 1234 }).toNumber() + ).toEqual(1234); + expect( + // @ts-expect-error We could receive these via JSON + () => new Int(registry, { ref_time: 1234, zoo: 4567 }).toNumber() + ).toThrow(/Unable to construct number from/); + }); + it('converts to Little Endian from the provided value', (): void => { expect( new Int(registry, -1234).toU8a() diff --git a/packages/types-known/src/spec/kusama.ts b/packages/types-known/src/spec/kusama.ts index 7c6a633ee5..93e65105e8 100644 --- a/packages/types-known/src/spec/kusama.ts +++ b/packages/types-known/src/spec/kusama.ts @@ -15,7 +15,8 @@ const sharedTypes = { Keys: 'SessionKeys6', ProxyType: { _enum: ['Any', 'NonTransfer', 'Governance', 'Staking', 'IdentityJudgement', 'CancelProxy', 'Auction'] - } + }, + Weight: 'u64' }; const addrIndicesTypes = { diff --git a/packages/types-known/src/spec/polkadot.ts b/packages/types-known/src/spec/polkadot.ts index 64020ea6f5..3e272fa365 100644 --- a/packages/types-known/src/spec/polkadot.ts +++ b/packages/types-known/src/spec/polkadot.ts @@ -23,7 +23,8 @@ const sharedTypes = { CancelProxy: 6, Auction: 7 } - } + }, + Weight: 'u64' }; const addrAccountIdTypes = { diff --git a/packages/types-known/src/spec/rococo.ts b/packages/types-known/src/spec/rococo.ts index 3f765045d3..0f48a4c0a6 100644 --- a/packages/types-known/src/spec/rococo.ts +++ b/packages/types-known/src/spec/rococo.ts @@ -14,7 +14,8 @@ import { objectSpread } from '@polkadot/util'; const sharedTypes = { DispatchErrorModule: 'DispatchErrorModuleU8', FullIdentification: '()', // No staking, only session (as per config) - Keys: 'SessionKeys7B' + Keys: 'SessionKeys7B', + Weight: 'u64' }; const versioned: OverrideVersionedType[] = [ diff --git a/packages/types-known/src/spec/statemint.ts b/packages/types-known/src/spec/statemint.ts index 8417d2b28f..3b2b12ff3a 100644 --- a/packages/types-known/src/spec/statemint.ts +++ b/packages/types-known/src/spec/statemint.ts @@ -21,7 +21,8 @@ const sharedTypes = { 'AssetManager', 'Staking' ] - } + }, + Weight: 'u64' }; // these are override types for Statemine, Statemint, Westmint diff --git a/packages/types-known/src/spec/westend.ts b/packages/types-known/src/spec/westend.ts index 70f9d47cb9..439cd7cf85 100644 --- a/packages/types-known/src/spec/westend.ts +++ b/packages/types-known/src/spec/westend.ts @@ -17,7 +17,8 @@ const sharedTypes = { Keys: 'SessionKeys6', ProxyType: { _enum: ['Any', 'NonTransfer', 'Staking', 'SudoBalances', 'IdentityJudgement', 'CancelProxy'] - } + }, + Weight: 'u64' }; const addrAccountIdTypes = { diff --git a/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts b/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts index 3ea8af00e7..766c615dfc 100644 --- a/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts +++ b/packages/types/src/metadata/PortableRegistry/PortableRegistry.ts @@ -47,7 +47,7 @@ const PATHS_ALIAS = splitNamespace([ 'sp_core::crypto::AccountId32', 'sp_runtime::generic::era::Era', 'sp_runtime::multiaddress::MultiAddress', - // weights v2 is a structure, treated as a u64 vis refTime + // weights v2 (1.5+) is a structure, treated as a u64 via refTime (these are used compact) 'frame_support::weights::weight_v2::Weight', // ethereum overrides (Frontier, Moonbeam, Polkadot claims) 'account::AccountId20',