Adapt GasPrice.fromString in @cosmjs/stargate and increase testing

This commit is contained in:
Simon Warta 2021-05-14 10:22:34 +02:00
parent b0b535ec95
commit 8f3260e1ac
3 changed files with 47 additions and 18 deletions

View File

@ -17,6 +17,8 @@ and this project adheres to
work with Hermes ([#801]; thanks [@AlexBHarley]). work with Hermes ([#801]; thanks [@AlexBHarley]).
- @cosmjs/launchpad: Adapt `GasPrice.fromString` denom pattern to Cosmos SDK - @cosmjs/launchpad: Adapt `GasPrice.fromString` denom pattern to Cosmos SDK
0.39 rules: reduce denom length to 16 and allow digits in denom. 0.39 rules: reduce denom length to 16 and allow digits in denom.
- @cosmjs/stargate: Adapt `GasPrice.fromString` denom pattern to Cosmos SDK 0.42
rules: allow lengths up to 128, allow upper case letters and digits.
[#800]: https://github.com/cosmos/cosmjs/issues/800 [#800]: https://github.com/cosmos/cosmjs/issues/800
[#801]: https://github.com/cosmos/cosmjs/issues/801 [#801]: https://github.com/cosmos/cosmjs/issues/801

View File

@ -14,12 +14,27 @@ describe("GasPrice", () => {
describe("fromString", () => { describe("fromString", () => {
it("works", () => { it("works", () => {
const inputs = ["3.14", "3", "0.14"]; const inputs: Record<string, { amount: string; denom: string }> = {
inputs.forEach((input) => { // Test amounts
const gasPrice = GasPrice.fromString(`${input}utest`); "3.14utest": { amount: "3.14", denom: "utest" },
expect(gasPrice.amount.toString()).toEqual(input); "3utest": { amount: "3", denom: "utest" },
expect(gasPrice.denom).toEqual("utest"); "0.14utest": { amount: "0.14", denom: "utest" },
}); // Test denoms
"0.14sht": { amount: "0.14", denom: "sht" },
"0.14testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest": {
amount: "0.14",
denom:
"testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest",
},
"0.14ucoin2": { amount: "0.14", denom: "ucoin2" },
// eslint-disable-next-line @typescript-eslint/naming-convention
"0.14FOOBAR": { amount: "0.14", denom: "FOOBAR" },
};
for (const [input, expected] of Object.entries(inputs)) {
const gasPrice = GasPrice.fromString(input);
expect(gasPrice.amount.toString()).withContext(`Input: ${input}`).toEqual(expected.amount);
expect(gasPrice.denom).withContext(`Input: ${input}`).toEqual(expected.denom);
}
}); });
it("errors for invalid gas price", () => { it("errors for invalid gas price", () => {
@ -30,17 +45,13 @@ describe("GasPrice", () => {
expect(() => GasPrice.fromString("234")).toThrowError(/Invalid gas price string/i); expect(() => GasPrice.fromString("234")).toThrowError(/Invalid gas price string/i);
expect(() => GasPrice.fromString("-234tkn")).toThrowError(/Invalid gas price string/i); expect(() => GasPrice.fromString("-234tkn")).toThrowError(/Invalid gas price string/i);
// Checks details of <denom> // Checks details of <denom>
expect(() => GasPrice.fromString("234t")).toThrowError( expect(() => GasPrice.fromString("234t")).toThrowError(/denom must be between 3 and 128 characters/i);
/denomination must be between 3 and 127 characters/i, expect(() => GasPrice.fromString("234tt")).toThrowError(/denom must be between 3 and 128 characters/i);
);
expect(() => GasPrice.fromString("234tt")).toThrowError(
/denomination must be between 3 and 127 characters/i,
);
expect(() => expect(() =>
GasPrice.fromString( GasPrice.fromString(
"234tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", "234ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt",
), ),
).toThrowError(/denomination must be between 3 and 127 characters/i); ).toThrowError(/denom must be between 3 and 128 characters/i);
// Checks details of <amount> // Checks details of <amount>
expect(() => GasPrice.fromString("3.utkn")).toThrowError(/Fractional part missing/i); expect(() => GasPrice.fromString("3.utkn")).toThrowError(/Fractional part missing/i);
expect(() => GasPrice.fromString("..utkn")).toThrowError(/More than one separator found/i); expect(() => GasPrice.fromString("..utkn")).toThrowError(/More than one separator found/i);

View File

@ -7,6 +7,18 @@ import { coins } from "@cosmjs/proto-signing";
*/ */
export type FeeTable = Record<string, StdFee>; export type FeeTable = Record<string, StdFee>;
/**
* Denom checker for the Cosmos SDK 0.42 denom pattern
* (https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/types/coin.go#L599-L601).
*
* This is like a regexp but with helpful error messages.
*/
function checkDenom(denom: string): void {
if (denom.length < 3 || denom.length > 128) {
throw new Error("Denom must be between 3 and 128 characters");
}
}
/** /**
* A gas price, i.e. the price of a single gas. This is typically a fraction of * A gas price, i.e. the price of a single gas. This is typically a fraction of
* the smallest fee token unit, such as 0.012utoken. * the smallest fee token unit, such as 0.012utoken.
@ -24,16 +36,20 @@ export class GasPrice {
/** /**
* Parses a gas price formatted as `<amount><denom>`, e.g. `GasPrice.fromString("0.012utoken")`. * Parses a gas price formatted as `<amount><denom>`, e.g. `GasPrice.fromString("0.012utoken")`.
*
* The denom must match the Cosmos SDK 0.42 pattern (https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/types/coin.go#L599-L601).
* See `GasPrice` in @cosmjs/stargate for a more generic matcher.
*
* Separators are not yet supported.
*/ */
public static fromString(gasPrice: string): GasPrice { public static fromString(gasPrice: string): GasPrice {
const matchResult = gasPrice.match(/^([0-9.]+?)([a-z]+)$/); // Use Decimal.fromUserInput and checkDenom for detailed checks and helpful error messages
const matchResult = gasPrice.match(/^([0-9.]+)([a-z][a-z0-9]*)$/i);
if (!matchResult) { if (!matchResult) {
throw new Error("Invalid gas price string"); throw new Error("Invalid gas price string");
} }
const [_, amount, denom] = matchResult; const [_, amount, denom] = matchResult;
if (denom.length < 3 || denom.length > 127) { checkDenom(denom);
throw new Error("Gas price denomination must be between 3 and 127 characters");
}
const fractionalDigits = 18; const fractionalDigits = 18;
const decimalAmount = Decimal.fromUserInput(amount, fractionalDigits); const decimalAmount = Decimal.fromUserInput(amount, fractionalDigits);
return new GasPrice(decimalAmount, denom); return new GasPrice(decimalAmount, denom);