diff --git a/packages/types/src/codec/utils/sortValues.ts b/packages/types/src/codec/utils/sortValues.ts index c71a0932b7..bdea1ede15 100644 --- a/packages/types/src/codec/utils/sortValues.ts +++ b/packages/types/src/codec/utils/sortValues.ts @@ -1,12 +1,13 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { BN } from '@polkadot/util'; import type { Codec } from '../../types'; import type { Enum } from '../Enum'; -import { isBn, isFunction, isNumber, stringify } from '@polkadot/util'; +import { bnToBn, isBigInt, isBn, isFunction, isNumber, stringify } from '@polkadot/util'; -type SortArg = Codec | Codec[] | number[] | number | Uint8Array +type SortArg = Codec | Codec[] | number[] | BN | bigint | number | Uint8Array; /** @internal **/ function isArrayLike (arg: SortArg): arg is Uint8Array | Codec[] | number[] { @@ -23,40 +24,48 @@ function isEnum (arg: SortArg): arg is Enum { return isCodec(arg) && isNumber((arg as Enum).index) && isCodec((arg as Enum).value); } +/** @internal */ +function isNumberLike (arg: SortArg): arg is BN | bigint | number { + return isNumber(arg) || isBn(arg) || isBigInt(arg); +} + +/** @internal */ +function sortArray (a: Uint8Array | Codec[] | number[], b: Uint8Array | Codec[] | number[]): number { + // Vec, Tuple, Bytes etc. + let sortRes = 0; + const minLen = Math.min(a.length, b.length); + + for (let i = 0; i < minLen; ++i) { + sortRes = sortAsc(a[i], b[i]); + + if (sortRes !== 0) { + return sortRes; + } + } + + return a.length - b.length; +} + /** -* Sort keys/values of BTreeSet/BTreeMap in acending order for encoding compatibility with Rust's BTreeSet/BTreeMap +* Sort keys/values of BTreeSet/BTreeMap in ascending order for encoding compatibility with Rust's BTreeSet/BTreeMap * (https://doc.rust-lang.org/stable/std/collections/struct.BTreeSet.html) * (https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html) */ export function sortAsc (a: V, b: V): number { - if (isNumber(a) && isNumber(b)) { - return a - b; + if (isNumberLike(a) && isNumberLike(b)) { + return bnToBn(a).cmp(bnToBn(b)); } else if (a instanceof Map && b instanceof Map) { return sortAsc(Array.from(a.values()), Array.from(b.values())); } else if (isEnum(a) && isEnum(b)) { return sortAsc(a.index, b.index) || sortAsc(a.value, b.value); } else if (isArrayLike(a) && isArrayLike(b)) { - // Vec, Tuple, Bytes etc. - let sortRes = 0; - const minLen = Math.min(a.length, b.length); - - for (let i = 0; i < minLen; ++i) { - sortRes = sortAsc(a[i], b[i]); - - if (sortRes !== 0) { - return sortRes; - } - } - - return a.length - b.length; - } else if (isBn(a) && isBn(b)) { - return a.cmp(b); + return sortArray(a, b); } else if (isCodec(a) && isCodec(b)) { // Text, Bool etc. return sortAsc(a.toU8a(true), b.toU8a(true)); } - throw new Error(`Attempting to sort unrecognized value: ${stringify(a)}`); + throw new Error(`Attempting to sort unrecognized values: ${stringify(a)} (typeof ${typeof a}) <-> ${stringify(b)} (typeof ${typeof b})`); } export function sortSet (set: Set): Set {