diff --git a/CHANGELOG.md b/CHANGELOG.md index e9b9f68e..e9d3a914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,11 @@ Launchpad. Two new classes are provided: `LedgerSigner` (for most use cases) and `LaunchpadLedger` for more fine-grained access. - @cosmjs/math: Add `.multiply` method to `Decimal` class. +- @cosmjs/math: Deprecate `Uint32.fromBigEndianBytes` in favour of + `Uint32.fromBytes`, which supports both big and little endian. +- @cosmjs/math: Deprecate `Uint64.fromBytesBigEndian` in favour of + `Uint64.fromBytes`, which supports both big and little endian. +- @cosmjs/math: Add `Uint32.fromString`. - @cosmjs/tendermint-rpc: Make `BroadcastTxCommitResponse.height` non-optional. - @cosmjs/tendermint-rpc: Make `TxProof.proof.leafHash` non-optional because it is always set. diff --git a/packages/math/src/integers.spec.ts b/packages/math/src/integers.spec.ts index 28ed202c..dd109782 100644 --- a/packages/math/src/integers.spec.ts +++ b/packages/math/src/integers.spec.ts @@ -2,6 +2,88 @@ import { Int53, Uint32, Uint53, Uint64 } from "./integers"; describe("Integers", () => { describe("Uint32", () => { + describe("fromBytes", () => { + it("can be constructed from to byte array", () => { + expect(Uint32.fromBytes([0, 0, 0, 0]).toNumber()).toEqual(0); + expect(Uint32.fromBytes([0, 0, 0, 1]).toNumber()).toEqual(1); + expect(Uint32.fromBytes([0, 0, 0, 42]).toNumber()).toEqual(42); + expect(Uint32.fromBytes([0x3b, 0x9a, 0xca, 0x00]).toNumber()).toEqual(1000000000); + expect(Uint32.fromBytes([0x7f, 0xff, 0xff, 0xff]).toNumber()).toEqual(2147483647); + expect(Uint32.fromBytes([0x80, 0x00, 0x00, 0x00]).toNumber()).toEqual(2147483648); + expect(Uint32.fromBytes([0xff, 0xff, 0xff, 0xff]).toNumber()).toEqual(4294967295); + }); + + it("can be constructed from Buffer", () => { + expect(Uint32.fromBytes(Buffer.from([0, 0, 0, 0])).toNumber()).toEqual(0); + expect(Uint32.fromBytes(Buffer.from([0, 0, 0, 1])).toNumber()).toEqual(1); + expect(Uint32.fromBytes(Buffer.from([0, 0, 0, 42])).toNumber()).toEqual(42); + expect(Uint32.fromBytes(Buffer.from([0x3b, 0x9a, 0xca, 0x00])).toNumber()).toEqual(1000000000); + expect(Uint32.fromBytes(Buffer.from([0x7f, 0xff, 0xff, 0xff])).toNumber()).toEqual(2147483647); + expect(Uint32.fromBytes(Buffer.from([0x80, 0x00, 0x00, 0x00])).toNumber()).toEqual(2147483648); + expect(Uint32.fromBytes(Buffer.from([0xff, 0xff, 0xff, 0xff])).toNumber()).toEqual(4294967295); + }); + + it("throws for invalid input length", () => { + expect(() => Uint32.fromBytes([])).toThrowError(/Invalid input length/); + expect(() => Uint32.fromBytes([0, 0, 0])).toThrowError(/Invalid input length/); + expect(() => Uint32.fromBytes([0, 0, 0, 0, 0])).toThrowError(/Invalid input length/); + }); + + it("throws for invalid values", () => { + expect(() => Uint32.fromBytes([0, 0, 0, -1])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBytes([0, 0, 0, 1.5])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBytes([0, 0, 0, 256])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBytes([0, 0, 0, NaN])).toThrowError(/Invalid value in byte/); + expect(() => Uint32.fromBytes([0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError( + /Invalid value in byte/, + ); + expect(() => Uint32.fromBytes([0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError( + /Invalid value in byte/, + ); + }); + + it("works for big and little endian", () => { + const b = Uint32.fromBytes([0x00, 0xa6, 0xb7, 0xd8], "be"); + expect(b.toNumber()).toEqual(0xa6b7d8); + + const l = Uint32.fromBytes([0xa6, 0xb7, 0xd8, 0x00], "le"); + expect(l.toNumber()).toEqual(0xd8b7a6); + }); + }); + + describe("fromString", () => { + it("can be constructed from string", () => { + { + const a = Uint32.fromString("0"); + expect(a.toNumber()).toEqual(0); + } + { + const a = Uint32.fromString("1"); + expect(a.toNumber()).toEqual(1); + } + { + const a = Uint32.fromString("01"); + expect(a.toNumber()).toEqual(1); + } + { + const a = Uint32.fromString("4294967295"); + expect(a.toNumber()).toEqual(4294967295); + } + }); + + it("throws for invalid string values", () => { + expect(() => Uint32.fromString(" 1")).toThrowError(/invalid string format/i); + expect(() => Uint32.fromString("-1")).toThrowError(/invalid string format/i); + expect(() => Uint32.fromString("+1")).toThrowError(/invalid string format/i); + expect(() => Uint32.fromString("1e6")).toThrowError(/invalid string format/i); + }); + + it("throws for string values exceeding uint32", () => { + expect(() => Uint32.fromString("4294967296")).toThrowError(/input not in uint32 range/i); + expect(() => Uint32.fromString("99999999999999999999")).toThrowError(/input not in uint32 range/i); + }); + }); + it("can be constructed", () => { expect(new Uint32(0)).toBeTruthy(); expect(new Uint32(1)).toBeTruthy(); @@ -79,55 +161,6 @@ describe("Integers", () => { ); }); }); - - describe("fromBigEndianBytes", () => { - it("can be constructed from to byte array", () => { - expect(Uint32.fromBigEndianBytes([0, 0, 0, 0]).toNumber()).toEqual(0); - expect(Uint32.fromBigEndianBytes([0, 0, 0, 1]).toNumber()).toEqual(1); - expect(Uint32.fromBigEndianBytes([0, 0, 0, 42]).toNumber()).toEqual(42); - expect(Uint32.fromBigEndianBytes([0x3b, 0x9a, 0xca, 0x00]).toNumber()).toEqual(1000000000); - expect(Uint32.fromBigEndianBytes([0x7f, 0xff, 0xff, 0xff]).toNumber()).toEqual(2147483647); - expect(Uint32.fromBigEndianBytes([0x80, 0x00, 0x00, 0x00]).toNumber()).toEqual(2147483648); - expect(Uint32.fromBigEndianBytes([0xff, 0xff, 0xff, 0xff]).toNumber()).toEqual(4294967295); - }); - - it("can be constructed from Buffer", () => { - expect(Uint32.fromBigEndianBytes(Buffer.from([0, 0, 0, 0])).toNumber()).toEqual(0); - expect(Uint32.fromBigEndianBytes(Buffer.from([0, 0, 0, 1])).toNumber()).toEqual(1); - expect(Uint32.fromBigEndianBytes(Buffer.from([0, 0, 0, 42])).toNumber()).toEqual(42); - expect(Uint32.fromBigEndianBytes(Buffer.from([0x3b, 0x9a, 0xca, 0x00])).toNumber()).toEqual( - 1000000000, - ); - expect(Uint32.fromBigEndianBytes(Buffer.from([0x7f, 0xff, 0xff, 0xff])).toNumber()).toEqual( - 2147483647, - ); - expect(Uint32.fromBigEndianBytes(Buffer.from([0x80, 0x00, 0x00, 0x00])).toNumber()).toEqual( - 2147483648, - ); - expect(Uint32.fromBigEndianBytes(Buffer.from([0xff, 0xff, 0xff, 0xff])).toNumber()).toEqual( - 4294967295, - ); - }); - - it("throws for invalid input length", () => { - expect(() => Uint32.fromBigEndianBytes([])).toThrowError(/Invalid input length/); - expect(() => Uint32.fromBigEndianBytes([0, 0, 0])).toThrowError(/Invalid input length/); - expect(() => Uint32.fromBigEndianBytes([0, 0, 0, 0, 0])).toThrowError(/Invalid input length/); - }); - - it("throws for invalid values", () => { - expect(() => Uint32.fromBigEndianBytes([0, 0, 0, -1])).toThrowError(/Invalid value in byte/); - expect(() => Uint32.fromBigEndianBytes([0, 0, 0, 1.5])).toThrowError(/Invalid value in byte/); - expect(() => Uint32.fromBigEndianBytes([0, 0, 0, 256])).toThrowError(/Invalid value in byte/); - expect(() => Uint32.fromBigEndianBytes([0, 0, 0, NaN])).toThrowError(/Invalid value in byte/); - expect(() => Uint32.fromBigEndianBytes([0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError( - /Invalid value in byte/, - ); - expect(() => Uint32.fromBigEndianBytes([0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError( - /Invalid value in byte/, - ); - }); - }); }); describe("Int53", () => { @@ -284,48 +317,50 @@ describe("Integers", () => { }); describe("Uint64", () => { - describe("fromBigEndianBytes", () => { + describe("fromBytes", () => { it("can be constructed from bytes", () => { - Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); - Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + Uint64.fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); }); it("can be constructed from Uint8Array", () => { - Uint64.fromBytesBigEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); + Uint64.fromBytes(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); }); it("throws for wrong number of bytes", () => { - expect(() => Uint64.fromBytesBigEndian([])).toThrowError(/invalid input length/i); - expect(() => Uint64.fromBytesBigEndian([0x00])).toThrowError(/invalid input length/i); - expect(() => Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError( + expect(() => Uint64.fromBytes([])).toThrowError(/invalid input length/i); + expect(() => Uint64.fromBytes([0x00])).toThrowError(/invalid input length/i); + expect(() => Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError( + /invalid input length/i, + ); + expect(() => Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError( /invalid input length/i, ); - expect(() => - Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), - ).toThrowError(/invalid input length/i); }); it("throws for wrong byte value", () => { - expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, 256])).toThrowError( + expect(() => Uint64.fromBytes([0, 0, 0, 0, 0, 0, 0, 256])).toThrowError(/invalid value in byte/i); + expect(() => Uint64.fromBytes([0, 0, 0, 0, 0, 0, 0, -1])).toThrowError(/invalid value in byte/i); + expect(() => Uint64.fromBytes([0, 0, 0, 0, 0, 0, 0, 1.5])).toThrowError(/invalid value in byte/i); + expect(() => Uint64.fromBytes([0, 0, 0, 0, 0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError( /invalid value in byte/i, ); - expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, -1])).toThrowError( + expect(() => Uint64.fromBytes([0, 0, 0, 0, 0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError( /invalid value in byte/i, ); - expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, 1.5])).toThrowError( - /invalid value in byte/i, - ); - expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError( - /invalid value in byte/i, - ); - expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError( - /invalid value in byte/i, - ); - expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NaN])).toThrowError( + expect(() => Uint64.fromBytes([0, 0, 0, 0, 0, 0, 0, Number.NaN])).toThrowError( /invalid value in byte/i, ); }); + + it("works for big and little endian", () => { + const b = Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xb7, 0xd8], "be"); + expect(b.toNumber()).toEqual(0xa6b7d8); + + const l = Uint64.fromBytes([0xa6, 0xb7, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00], "le"); + expect(l.toNumber()).toEqual(0xd8b7a6); + }); }); describe("fromString", () => { @@ -396,77 +431,77 @@ describe("Integers", () => { }); it("can export bytes (big endian)", () => { - expect( - Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian(), - ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); - expect( - Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesBigEndian(), - ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])); - expect( - Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian(), - ).toEqual(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); - expect( - Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesBigEndian(), - ).toEqual(new Uint8Array([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d])); - expect( - Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesBigEndian(), - ).toEqual(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])); + expect(Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian()).toEqual( + new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + ); + expect(Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesBigEndian()).toEqual( + new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), + ); + expect(Uint64.fromBytes([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian()).toEqual( + new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + ); + expect(Uint64.fromBytes([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesBigEndian()).toEqual( + new Uint8Array([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]), + ); + expect(Uint64.fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesBigEndian()).toEqual( + new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + ); }); it("can export bytes (little endian)", () => { expect( - Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian(), + Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian(), ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); expect( - Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesLittleEndian(), + Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesLittleEndian(), ).toEqual(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); expect( - Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian(), + Uint64.fromBytes([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian(), ).toEqual(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])); expect( - Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesLittleEndian(), + Uint64.fromBytes([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesLittleEndian(), ).toEqual(new Uint8Array([0x0d, 0x4e, 0x20, 0xa9, 0x5f, 0xbc, 0x22, 0xab])); expect( - Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesLittleEndian(), + Uint64.fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesLittleEndian(), ).toEqual(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])); }); it("can export strings", () => { { - const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + const a = Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); expect(a.toString()).toEqual("0"); } { - const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + const a = Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); expect(a.toString()).toEqual("1"); } { - const a = Uint64.fromBytesBigEndian([0x8a, 0xc7, 0x23, 0x04, 0x89, 0xe7, 0xff, 0xff]); + const a = Uint64.fromBytes([0x8a, 0xc7, 0x23, 0x04, 0x89, 0xe7, 0xff, 0xff]); expect(a.toString()).toEqual("9999999999999999999"); } { - const a = Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + const a = Uint64.fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); expect(a.toString()).toEqual("18446744073709551615"); } }); it("can export numbers", () => { { - const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + const a = Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); expect(a.toNumber()).toEqual(0); } { - const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + const a = Uint64.fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); expect(a.toNumber()).toEqual(1); } { // value too large for 53 bit integer - const a = Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + const a = Uint64.fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); } { // Number.MAX_SAFE_INTEGER + 1 - const a = Uint64.fromBytesBigEndian([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + const a = Uint64.fromBytes([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); } }); diff --git a/packages/math/src/integers.ts b/packages/math/src/integers.ts index 155252d3..573efefa 100644 --- a/packages/math/src/integers.ts +++ b/packages/math/src/integers.ts @@ -14,8 +14,27 @@ interface WithByteConverters { readonly toBytesLittleEndian: () => Uint8Array; } +interface IntegerStatic { + readonly fromString: (str: string) => T; +} + +interface FixedLengthIntegerStatic { + readonly fromBytes: (bytes: ArrayLike, endianess: "be" | "le") => T; +} + export class Uint32 implements Integer, WithByteConverters { + /** @deprecated use Uint32.fromBytes */ public static fromBigEndianBytes(bytes: ArrayLike): Uint32 { + return Uint32.fromBytes(bytes); + } + + /** + * Creates a Uint32 from a fixed length byte array. + * + * @param bytes a list of exactly 4 bytes + * @param endianess defaults to big endian + */ + public static fromBytes(bytes: ArrayLike, endianess: "be" | "le" = "be"): Uint32 { if (bytes.length !== 4) { throw new Error("Invalid input length. Expected 4 bytes."); } @@ -26,9 +45,18 @@ export class Uint32 implements Integer, WithByteConverters { } } + const beBytes = endianess === "be" ? bytes : Array.from(bytes).reverse(); + // Use mulitiplication instead of shifting since bitwise operators are defined // on SIGNED int32 in JavaScript and we don't want to risk surprises - return new Uint32(bytes[0] * 2 ** 24 + bytes[1] * 2 ** 16 + bytes[2] * 2 ** 8 + bytes[3]); + return new Uint32(beBytes[0] * 2 ** 24 + beBytes[1] * 2 ** 16 + beBytes[2] * 2 ** 8 + beBytes[3]); + } + + public static fromString(str: string): Uint32 { + if (!str.match(/^[0-9]+$/)) { + throw new Error("Invalid string format"); + } + return new Uint32(Number.parseInt(str, 10)); } protected readonly data: number; @@ -142,7 +170,18 @@ export class Uint53 implements Integer { } export class Uint64 implements Integer, WithByteConverters { + /** @deprecated use Uint64.fromBytes */ public static fromBytesBigEndian(bytes: ArrayLike): Uint64 { + return Uint64.fromBytes(bytes); + } + + /** + * Creates a Uint64 from a fixed length byte array. + * + * @param bytes a list of exactly 8 bytes + * @param endianess defaults to big endian + */ + public static fromBytes(bytes: ArrayLike, endianess: "be" | "le" = "be"): Uint64 { if (bytes.length !== 8) { throw new Error("Invalid input length. Expected 8 bytes."); } @@ -153,12 +192,8 @@ export class Uint64 implements Integer, WithByteConverters { } } - const asArray: number[] = []; - for (let i = 0; i < bytes.length; ++i) { - asArray.push(bytes[i]); - } - - return new Uint64(new BN([...asArray])); + const beBytes = endianess === "be" ? Array.from(bytes) : Array.from(bytes).reverse(); + return new Uint64(new BN(beBytes)); } public static fromString(str: string): Uint64 { @@ -214,3 +249,10 @@ export class Uint64 implements Integer, WithByteConverters { return this.data.toNumber(); } } + +// Assign classes to unused variables in order to verify static interface conformance at compile time. +// Workaround for https://github.com/microsoft/TypeScript/issues/33892 +const _int53Class: IntegerStatic = Int53; +const _uint53Class: IntegerStatic = Uint53; +const _uint32Class: IntegerStatic & FixedLengthIntegerStatic = Uint32; +const _uint64Class: IntegerStatic & FixedLengthIntegerStatic = Uint64; diff --git a/packages/math/types/integers.d.ts b/packages/math/types/integers.d.ts index 39324e96..b1078dcf 100644 --- a/packages/math/types/integers.d.ts +++ b/packages/math/types/integers.d.ts @@ -8,7 +8,16 @@ interface WithByteConverters { readonly toBytesLittleEndian: () => Uint8Array; } export declare class Uint32 implements Integer, WithByteConverters { + /** @deprecated use Uint32.fromBytes */ static fromBigEndianBytes(bytes: ArrayLike): Uint32; + /** + * Creates a Uint32 from a fixed length byte array. + * + * @param bytes a list of exactly 4 bytes + * @param endianess defaults to big endian + */ + static fromBytes(bytes: ArrayLike, endianess?: "be" | "le"): Uint32; + static fromString(str: string): Uint32; protected readonly data: number; constructor(input: number); toBytesBigEndian(): Uint8Array; @@ -31,7 +40,15 @@ export declare class Uint53 implements Integer { toString(): string; } export declare class Uint64 implements Integer, WithByteConverters { + /** @deprecated use Uint64.fromBytes */ static fromBytesBigEndian(bytes: ArrayLike): Uint64; + /** + * Creates a Uint64 from a fixed length byte array. + * + * @param bytes a list of exactly 8 bytes + * @param endianess defaults to big endian + */ + static fromBytes(bytes: ArrayLike, endianess?: "be" | "le"): Uint64; static fromString(str: string): Uint64; static fromNumber(input: number): Uint64; private readonly data; diff --git a/packages/stargate/src/queries/ibc.ts b/packages/stargate/src/queries/ibc.ts index 550fd267..aaa843e7 100644 --- a/packages/stargate/src/queries/ibc.ts +++ b/packages/stargate/src/queries/ibc.ts @@ -111,7 +111,7 @@ export function setupIbcExtension(base: QueryClient): IbcExtension { // key: https://github.com/cosmos/cosmos-sdk/blob/ef0a7344af345882729598bc2958a21143930a6b/x/ibc/24-host/keys.go#L133-L136 const key = toAscii(`seqAcks/ports/${portId}/channels/${channelId}/nextSequenceAck`); const responseData = await base.queryVerified("ibc", key); - return responseData.length ? Uint64.fromBytesBigEndian(responseData).toNumber() : null; + return responseData.length ? Uint64.fromBytes(responseData).toNumber() : null; }, unverified: {