From 09d5cd64bc1dcf842c96cb47e202068237a8b401 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 23 Mar 2021 16:33:04 +0100 Subject: [PATCH] proto-signing: Copy coins from launchpad --- packages/proto-signing/src/coins.spec.ts | 107 +++++++++++++++++++++++ packages/proto-signing/src/coins.ts | 47 ++++++++++ packages/proto-signing/src/index.ts | 1 + 3 files changed, 155 insertions(+) create mode 100644 packages/proto-signing/src/coins.spec.ts create mode 100644 packages/proto-signing/src/coins.ts diff --git a/packages/proto-signing/src/coins.spec.ts b/packages/proto-signing/src/coins.spec.ts new file mode 100644 index 00000000..5f74c4cb --- /dev/null +++ b/packages/proto-signing/src/coins.spec.ts @@ -0,0 +1,107 @@ +import { coin, coins, parseCoins } from "./coins"; + +describe("coins", () => { + describe("coin", () => { + it("works for basic values", () => { + expect(coin(123, "utoken")).toEqual({ amount: "123", denom: "utoken" }); + expect(coin(123.0, "utoken")).toEqual({ amount: "123", denom: "utoken" }); + expect(coin(Number.MAX_SAFE_INTEGER, "utoken")).toEqual({ + amount: "9007199254740991", + denom: "utoken", + }); + expect(coin(+0, "utoken")).toEqual({ amount: "0", denom: "utoken" }); + expect(coin(-0, "utoken")).toEqual({ amount: "0", denom: "utoken" }); + }); + + it("throws for non-safe-integer values", () => { + expect(() => coin(1.23, "utoken")).toThrow(); + expect(() => coin(NaN, "utoken")).toThrow(); + expect(() => coin(Number.POSITIVE_INFINITY, "utoken")).toThrow(); + expect(() => coin(Number.MAX_SAFE_INTEGER + 1, "utoken")).toThrow(); + }); + + it("throws for negative values", () => { + expect(() => coin(-1, "utoken")).toThrow(); + expect(() => coin(Number.MIN_SAFE_INTEGER, "utoken")).toThrow(); + expect(() => coin(Number.NEGATIVE_INFINITY, "utoken")).toThrow(); + }); + }); + + describe("coins", () => { + it("returns one element array of coin", () => { + expect(coins(123, "utoken")).toEqual([{ amount: "123", denom: "utoken" }]); + }); + }); + + describe("parseCoins", () => { + it("works for empty", () => { + expect(parseCoins("")).toEqual([]); + }); + + it("works for one element", () => { + expect(parseCoins("7643ureef")).toEqual([ + { + amount: "7643", + denom: "ureef", + }, + ]); + }); + + it("works for two", () => { + expect(parseCoins("819966000ucosm,700000000ustake")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + }); + + it("ignores empty elements", () => { + // start + expect(parseCoins(",819966000ucosm,700000000ustake")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + // middle + expect(parseCoins("819966000ucosm,,700000000ustake")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + // end + expect(parseCoins("819966000ucosm,700000000ustake,")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + }); + + it("throws for invalid inputs", () => { + // denom missing + expect(() => parseCoins("3456")).toThrowError(/invalid coin string/i); + + // amount missing + expect(() => parseCoins("ucosm")).toThrowError(/invalid coin string/i); + }); + }); +}); diff --git a/packages/proto-signing/src/coins.ts b/packages/proto-signing/src/coins.ts new file mode 100644 index 00000000..ec6881c0 --- /dev/null +++ b/packages/proto-signing/src/coins.ts @@ -0,0 +1,47 @@ +import { Uint53, Uint64 } from "@cosmjs/math"; + +/** + * This is the same as Coin from @cosmjs/launchpad but those might diverge in the future. + */ +export interface Coin { + readonly denom: string; + readonly amount: string; +} + +/** + * Creates a coin. + * + * This is the same as coin from @cosmjs/launchpad but those might diverge in the future. + */ +export function coin(amount: number, denom: string): Coin { + return { amount: new Uint53(amount).toString(), denom: denom }; +} + +/** + * Creates a list of coins with one element + * + * This is the same as coins from @cosmjs/launchpad but those might diverge in the future. + */ +export function coins(amount: number, denom: string): Coin[] { + return [coin(amount, denom)]; +} + +/** + * Takes a coins list like "819966000ucosm,700000000ustake" and parses it + * + * This is the same as parseCoins from @cosmjs/launchpad but those might diverge in the future. + */ +export function parseCoins(input: string): Coin[] { + return input + .replace(/\s/g, "") + .split(",") + .filter(Boolean) + .map((part) => { + const match = part.match(/^([0-9]+)([a-zA-Z]+)/); + if (!match) throw new Error("Got an invalid coin string"); + return { + amount: Uint64.fromString(match[1]).toString(), + denom: match[2], + }; + }); +} diff --git a/packages/proto-signing/src/index.ts b/packages/proto-signing/src/index.ts index 0b11ea43..56518dd2 100644 --- a/packages/proto-signing/src/index.ts +++ b/packages/proto-signing/src/index.ts @@ -1,3 +1,4 @@ +export { Coin, coin, coins, parseCoins } from "./coins"; export { isPbjsGeneratedType, isTsProtoGeneratedType,