Merge pull request #923 from cosmos/let-coin-support-strings

Let coin/coins take amount as number | string
This commit is contained in:
Simon Warta 2021-11-17 09:29:11 +01:00 committed by GitHub
commit 57fac40b37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 11 deletions

View File

@ -6,6 +6,12 @@ and this project adheres to
## [Unreleased]
### Added
- @cosmjs/amino: The `coin` and `coins` helpers now support both `number` and
`string` as input types for the amount. This is useful if your values exceed
the safe integer range.
## [0.26.4] - 2021-10-28
### Fixed

View File

@ -2,7 +2,7 @@ import { coin, coins, parseCoins } from "./coins";
describe("coins", () => {
describe("coin", () => {
it("works for basic values", () => {
it("works for number amounts", () => {
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({
@ -14,22 +14,56 @@ describe("coins", () => {
});
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();
expect(() => coin(1.23, "utoken")).toThrowError(/Given amount is not a safe integer/i);
expect(() => coin(NaN, "utoken")).toThrowError(/Given amount is not a safe integer/i);
expect(() => coin(Number.POSITIVE_INFINITY, "utoken")).toThrowError(
/Given amount is not a safe integer/i,
);
expect(() => coin(Number.MAX_SAFE_INTEGER + 1, "utoken")).toThrowError(
/Given amount is not a safe integer/i,
);
});
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();
expect(() => coin(-1, "utoken")).toThrowError(/Given amount is not a safe integer/i);
expect(() => coin(Number.MIN_SAFE_INTEGER, "utoken")).toThrowError(
/Given amount is not a safe integer/i,
);
expect(() => coin(Number.NEGATIVE_INFINITY, "utoken")).toThrowError(
/Given amount is not a safe integer/i,
);
});
it("works for string amounts", () => {
expect(coin("0", "utoken")).toEqual({ amount: "0", denom: "utoken" });
expect(coin("1", "utoken")).toEqual({ amount: "1", denom: "utoken" });
expect(coin("00123", "utoken")).toEqual({ amount: "123", denom: "utoken" });
expect(coin("12300", "utoken")).toEqual({ amount: "12300", denom: "utoken" });
expect(coin("9007199254740992", "utoken")).toEqual({ amount: "9007199254740992", denom: "utoken" });
// ETH supply (~118 mio ETH)
expect(coin("118273505060000000000000000", "wei")).toEqual({
amount: "118273505060000000000000000",
denom: "wei",
});
});
it("throws for invalid amount strings", () => {
expect(() => coin("-1", "utoken")).toThrowError(/Invalid unsigned integer string format/i);
expect(() => coin("0x01", "utoken")).toThrowError(/Invalid unsigned integer string format/i);
expect(() => coin("NaN", "utoken")).toThrowError(/Invalid unsigned integer string format/i);
expect(() => coin("1.0", "utoken")).toThrowError(/Invalid unsigned integer string format/i);
expect(() => coin("1 ", "utoken")).toThrowError(/Invalid unsigned integer string format/i);
expect(() => coin(" 1", "utoken")).toThrowError(/Invalid unsigned integer string format/i);
expect(() => coin("1.1827350506e+26", "utoken")).toThrowError(
/Invalid unsigned integer string format/i,
);
});
});
describe("coins", () => {
it("returns one element array of coin", () => {
expect(coins(123, "utoken")).toEqual([{ amount: "123", denom: "utoken" }]);
expect(coins("123", "utoken")).toEqual([{ amount: "123", denom: "utoken" }]);
});
});

View File

@ -7,15 +7,39 @@ export interface Coin {
/**
* Creates a coin.
*
* If your values do not exceed the safe integer range of JS numbers (53 bit),
* you can use the number type here. This is the case for all typical Cosmos SDK
* chains that use the default 6 decimals.
*
* In case you need to supportr larger values, use unsigned integer strings instead.
*/
export function coin(amount: number, denom: string): Coin {
return { amount: new Uint53(amount).toString(), denom: denom };
export function coin(amount: number | string, denom: string): Coin {
let outAmount: string;
if (typeof amount === "number") {
try {
outAmount = new Uint53(amount).toString();
} catch (_err) {
throw new Error(
"Given amount is not a safe integer. Consider using a string instead to overcome the limitations of JS numbers.",
);
}
} else {
if (!amount.match(/^[0-9]+$/)) {
throw new Error("Invalid unsigned integer string format");
}
outAmount = amount.replace(/^0*/, "") || "0";
}
return {
amount: outAmount,
denom: denom,
};
}
/**
* Creates a list of coins with one element.
*/
export function coins(amount: number, denom: string): Coin[] {
export function coins(amount: number | string, denom: string): Coin[] {
return [coin(amount, denom)];
}