diff --git a/CHANGELOG.md b/CHANGELOG.md index 580a20fa60..5e9e0a319b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,9 +23,9 @@ - @cosmjs/faucet: Environmental variable `FAUCET_FEE` renamed to `FAUCET_GAS_PRICE` and now only accepts one token. Environmental variable `FAUCET_GAS` renamed to `FAUCET_GAS_LIMIT`. -- @cosmjs/faucet: `/credit` API now accepts either `denom` (base token) or as - before `ticker` (unit token). Environmental variables specifying credit - amounts now need to use uppercase denom. +- @cosmjs/faucet: `/credit` API now expects `denom` (base token) instead of + `ticker` (unit token). Environmental variables specifying credit amounts now + need to use uppercase denom. - @cosmjs/launchpad: Rename `FeeTable` type to `CosmosFeeTable` and export a new more generic type `FeeTable`. - @cosmjs/launchpad: Add new class `GasPrice`, new helper type `GasLimits` and diff --git a/packages/faucet/README.md b/packages/faucet/README.md index c191adbf06..3efa573a43 100644 --- a/packages/faucet/README.md +++ b/packages/faucet/README.md @@ -19,8 +19,8 @@ yarn dev-start Advanced users that want to provide their custom config can start as follows: ``` -FAUCET_CREDIT_AMOUNT_COSM=10 \ - FAUCET_CREDIT_AMOUNT_STAKE=5 \ +FAUCET_CREDIT_AMOUNT_UCOSM=10000000 \ + FAUCET_CREDIT_AMOUNT_USTAKE=5000000 \ FAUCET_CONCURRENCY=3 \ FAUCET_MNEMONIC="economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone" \ ./bin/cosmos-faucet start "http://localhost:1317" @@ -53,11 +53,10 @@ FAUCET_GAS_LIMIT Gas limit for send transactions. Defaults to 80000. FAUCET_MNEMONIC Secret mnemonic that serves as the base secret for the faucet HD accounts FAUCET_ADDRESS_PREFIX The bech32 address prefix. Defaults to "cosmos". -FAUCET_TOKENS A comma separated list of tokens configs in the format - {DISPLAY}=10^{DIGITS}{base}, e.g. - "ATOM=10^6uatom" or "COSM = 10^6ucosm, STAKE = 10^3mstake". +FAUCET_TOKENS A comma separated list of token denoms, e.g. + "uatom" or "ucosm, mstake". FAUCET_CREDIT_AMOUNT_TKN Send this amount of TKN to a user requesting TKN. TKN is - a placeholder for the token ticker. Defaults to 10. + a placeholder for the token's denom. Defaults to 10000000. FAUCET_REFILL_FACTOR Send factor times credit amount on refilling. Defauls to 8. FAUCET_REFILL_THRESHOLD Refill when balance gets below factor times credit amount. Defaults to 20. @@ -124,7 +123,7 @@ situation is different. ``` curl --header "Content-Type: application/json" \ --request POST \ - --data '{"ticker":"ISA","address":"cosmos1yre6ac7qfgyfgvh58ph0rgw627rhw766y430qq"}' \ + --data '{"denom":"ucosm","address":"cosmos1yre6ac7qfgyfgvh58ph0rgw627rhw766y430qq"}' \ http://localhost:8000/credit ``` diff --git a/packages/faucet/package.json b/packages/faucet/package.json index faaf81a941..1335221529 100644 --- a/packages/faucet/package.json +++ b/packages/faucet/package.json @@ -37,7 +37,7 @@ "test": "yarn build-or-skip && yarn test-node", "coverage": "nyc --reporter=text --reporter=lcov yarn test --quiet", "start-dev": "FAUCET_CREDIT_AMOUNT_UCOSM=10000000 FAUCET_CREDIT_AMOUNT_USTAKE=5000000 FAUCET_CONCURRENCY=3 FAUCET_MNEMONIC=\"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone\" ./bin/cosmos-faucet start \"http://localhost:1317\"", - "start-coralnet": "FAUCET_ADDRESS_PREFIX=coral FAUCET_TOKENS=\"SHELL=10^6ushell, REEF=10^6ureef\" FAUCET_CREDIT_AMOUNT_USHELL=10000000 FAUCET_CREDIT_AMOUNT_UREEF=2000000 FAUCET_CONCURRENCY=3 FAUCET_MNEMONIC=\"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone\" ./bin/cosmos-faucet start \"https://lcd.coralnet.cosmwasm.com\"" + "start-coralnet": "FAUCET_ADDRESS_PREFIX=coral FAUCET_TOKENS=\"ushell,ureef\" FAUCET_CREDIT_AMOUNT_USHELL=10000000 FAUCET_CREDIT_AMOUNT_UREEF=2000000 FAUCET_CONCURRENCY=3 FAUCET_MNEMONIC=\"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone\" ./bin/cosmos-faucet start \"https://lcd.coralnet.cosmwasm.com\"" }, "dependencies": { "@cosmjs/crypto": "^0.22.3", diff --git a/packages/faucet/src/actions/help.ts b/packages/faucet/src/actions/help.ts index 1c6a73b763..74cc23b919 100644 --- a/packages/faucet/src/actions/help.ts +++ b/packages/faucet/src/actions/help.ts @@ -26,11 +26,10 @@ FAUCET_GAS_LIMIT Gas limit for send transactions. Defaults to 80000. FAUCET_MNEMONIC Secret mnemonic that serves as the base secret for the faucet HD accounts FAUCET_ADDRESS_PREFIX The bech32 address prefix. Defaults to "cosmos". -FAUCET_TOKENS A comma separated list of tokens configs in the format - {DISPLAY}=10^{DIGITS}{base}, e.g. - "ATOM=10^6uatom" or "COSM = 10^6ucosm, STAKE = 10^3mstake". +FAUCET_TOKENS A comma separated list of token denoms, e.g. + "uatom" or "ucosm, mstake". FAUCET_CREDIT_AMOUNT_TKN Send this amount of TKN to a user requesting TKN. TKN is - a placeholder for the token ticker. Defaults to 10. + a placeholder for the token's denom. Defaults to 10000000. FAUCET_REFILL_FACTOR Send factor times credit amount on refilling. Defauls to 8. FAUCET_REFILL_THRESHOLD Refill when balance gets below factor times credit amount. Defaults to 20. diff --git a/packages/faucet/src/actions/start.ts b/packages/faucet/src/actions/start.ts index 63c13feffe..f271d09744 100644 --- a/packages/faucet/src/actions/start.ts +++ b/packages/faucet/src/actions/start.ts @@ -28,10 +28,10 @@ export async function start(args: readonly string[]): Promise { constants.concurrency, true, ); - const chainTokens = faucet.loadTokenTickers(); + const chainTokens = faucet.configuredTokens(); console.info("Chain tokens:", chainTokens); const accounts = await faucet.loadAccounts(); - logAccountsState(accounts, constants.tokenConfig); + logAccountsState(accounts); let availableTokens = await faucet.availableTokens(); console.info("Available tokens:", availableTokens); setInterval(async () => { diff --git a/packages/faucet/src/api/requestparser.spec.ts b/packages/faucet/src/api/requestparser.spec.ts index 1171ff5ac1..c8ae110c92 100644 --- a/packages/faucet/src/api/requestparser.spec.ts +++ b/packages/faucet/src/api/requestparser.spec.ts @@ -6,9 +6,16 @@ describe("RequestParser", () => { expect(RequestParser.parseCreditBody(body)).toEqual({ address: "abc", denom: "utkn" }); }); - it("can process valid credit request with ticker", () => { - const body = { address: "abc", ticker: "TKN" }; - expect(RequestParser.parseCreditBody(body)).toEqual({ address: "abc", ticker: "TKN" }); + it("throws helpful error message when ticker is found", () => { + const oldBody = { address: "abc", ticker: "TKN" }; + expect(() => RequestParser.parseCreditBody(oldBody)).toThrowError( + /The 'ticker' field was removed in CosmJS 0.23. Please use 'denom' instead./i, + ); + + const confusedBody = { address: "abc", ticker: "TKN", denom: "utkn" }; + expect(() => RequestParser.parseCreditBody(confusedBody)).toThrowError( + /The 'ticker' field was removed in CosmJS 0.23. Please use 'denom' instead./i, + ); }); it("throws for invalid credit requests", () => { @@ -26,44 +33,32 @@ describe("RequestParser", () => { // address unset { - const body = { ticker: "TKN" }; + const body = { denom: "utkn" }; expect(() => RequestParser.parseCreditBody(body)).toThrowError(/Property 'address' must be a string/i); } // address wrong type { - const body = { address: true, ticker: "TKN" }; + const body = { address: true, denom: "utkn" }; expect(() => RequestParser.parseCreditBody(body)).toThrowError(/Property 'address' must be a string/i); } // address empty { - const body = { address: "", ticker: "TKN" }; + const body = { address: "", denom: "utkn" }; expect(() => RequestParser.parseCreditBody(body)).toThrowError(/Property 'address' must not be empty/i); } - // denom and ticker unset + // denom unset { const body = { address: "abc" }; - expect(() => RequestParser.parseCreditBody(body)).toThrowError( - /Exactly one of properties 'denom' or 'ticker' must be a string/i, - ); - } - - // denom and ticker both set - { - const body = { address: "abc", denom: "ustake", ticker: "COSM" }; - expect(() => RequestParser.parseCreditBody(body)).toThrowError( - /Exactly one of properties 'denom' or 'ticker' must be a string/i, - ); + expect(() => RequestParser.parseCreditBody(body)).toThrowError(/Property 'denom' must be a string/i); } // denom wrong type { const body = { address: "abc", denom: true }; - expect(() => RequestParser.parseCreditBody(body)).toThrowError( - /Exactly one of properties 'denom' or 'ticker' must be a string/i, - ); + expect(() => RequestParser.parseCreditBody(body)).toThrowError(/Property 'denom' must be a string/i); } // denom empty @@ -71,19 +66,5 @@ describe("RequestParser", () => { const body = { address: "abc", denom: "" }; expect(() => RequestParser.parseCreditBody(body)).toThrowError(/Property 'denom' must not be empty/i); } - - // ticker wrong type - { - const body = { address: "abc", ticker: true }; - expect(() => RequestParser.parseCreditBody(body)).toThrowError( - /Exactly one of properties 'denom' or 'ticker' must be a string/i, - ); - } - - // ticker empty - { - const body = { address: "abc", ticker: "" }; - expect(() => RequestParser.parseCreditBody(body)).toThrowError(/Property 'ticker' must not be empty/i); - } }); }); diff --git a/packages/faucet/src/api/requestparser.ts b/packages/faucet/src/api/requestparser.ts index 1455b05f48..2ff99d3c9d 100644 --- a/packages/faucet/src/api/requestparser.ts +++ b/packages/faucet/src/api/requestparser.ts @@ -2,7 +2,7 @@ import { isNonNullObject } from "@cosmjs/utils"; import { HttpError } from "./httperror"; -export interface CreditRequestBodyDataWithDenom { +export interface CreditRequestBodyData { /** The base denomination */ readonly denom: string; /** The recipient address */ @@ -16,14 +16,6 @@ export interface CreditRequestBodyDataWithTicker { readonly address: string; } -export type CreditRequestBodyData = CreditRequestBodyDataWithDenom | CreditRequestBodyDataWithTicker; - -export function isCreditRequestBodyDataWithDenom( - data: CreditRequestBodyData, -): data is CreditRequestBodyDataWithDenom { - return typeof (data as CreditRequestBodyDataWithDenom).denom === "string"; -} - export class RequestParser { public static parseCreditBody(body: unknown): CreditRequestBodyData { if (!isNonNullObject(body) || Array.isArray(body)) { @@ -32,6 +24,10 @@ export class RequestParser { const { address, denom, ticker } = body as any; + if (typeof ticker !== "undefined") { + throw new HttpError(400, "The 'ticker' field was removed in CosmJS 0.23. Please use 'denom' instead."); + } + if (typeof address !== "string") { throw new HttpError(400, "Property 'address' must be a string."); } @@ -40,29 +36,17 @@ export class RequestParser { throw new HttpError(400, "Property 'address' must not be empty."); } - if ( - (typeof denom !== "string" && typeof ticker !== "string") || - (typeof denom === "string" && typeof ticker === "string") - ) { - throw new HttpError(400, "Exactly one of properties 'denom' or 'ticker' must be a string"); + if (typeof denom !== "string") { + throw new HttpError(400, "Property 'denom' must be a string."); } - if (typeof ticker === "string" && ticker.length === 0) { - throw new HttpError(400, "Property 'ticker' must not be empty."); - } - - if (typeof denom === "string" && denom.length === 0) { + if (denom.length === 0) { throw new HttpError(400, "Property 'denom' must not be empty."); } - return denom - ? { - address: address, - denom: denom, - } - : { - address: address, - ticker: ticker, - }; + return { + address: address, + denom: denom, + }; } } diff --git a/packages/faucet/src/api/webserver.ts b/packages/faucet/src/api/webserver.ts index 474501eb76..446ee465f9 100644 --- a/packages/faucet/src/api/webserver.ts +++ b/packages/faucet/src/api/webserver.ts @@ -6,7 +6,7 @@ import { isValidAddress } from "../addresses"; import * as constants from "../constants"; import { Faucet } from "../faucet"; import { HttpError } from "./httperror"; -import { isCreditRequestBodyDataWithDenom, RequestParser } from "./requestparser"; +import { RequestParser } from "./requestparser"; /** This will be passed 1:1 to the user */ export interface ChainConstants { @@ -35,7 +35,7 @@ export class Webserver { case "/status": { const [holder, ...distributors] = await faucet.loadAccounts(); const availableTokens = await faucet.availableTokens(); - const chainTokens = faucet.loadTokenTickers(); + const chainTokens = faucet.configuredTokens(); context.response.body = { status: "ok", ...chainConstants, @@ -59,30 +59,20 @@ export class Webserver { const requestBody = context.request.body; const creditBody = RequestParser.parseCreditBody(requestBody); - const { address } = creditBody; - let denom: string | undefined; - let ticker: string | undefined; - if (isCreditRequestBodyDataWithDenom(creditBody)) { - ({ denom } = creditBody); - } else { - ({ ticker } = creditBody); - } + const { address, denom } = creditBody; if (!isValidAddress(address, constants.addressPrefix)) { throw new HttpError(400, "Address is not in the expected format for this chain."); } const availableTokens = await faucet.availableTokens(); - const matchingToken = availableTokens.find( - (token) => token.denom === denom || token.tickerSymbol === ticker, - ); - if (matchingToken === undefined) { - const tokens = JSON.stringify(availableTokens); - throw new HttpError(422, `Token is not available. Available tokens are: ${tokens}`); + const matchingDenom = availableTokens.find((availableDenom) => availableDenom === denom); + if (matchingDenom === undefined) { + throw new HttpError(422, `Token is not available. Available tokens are: ${availableTokens}`); } try { - await faucet.credit(address, matchingToken.denom); + await faucet.credit(address, matchingDenom); } catch (e) { console.error(e); throw new HttpError(500, "Sending tokens failed"); diff --git a/packages/faucet/src/constants.ts b/packages/faucet/src/constants.ts index 32822e7006..e8be3084d9 100644 --- a/packages/faucet/src/constants.ts +++ b/packages/faucet/src/constants.ts @@ -14,5 +14,5 @@ export const port: number = Number.parseInt(process.env.FAUCET_PORT || "", 10) | export const mnemonic: string | undefined = process.env.FAUCET_MNEMONIC; export const addressPrefix = process.env.FAUCET_ADDRESS_PREFIX || "cosmos"; export const tokenConfig: TokenConfiguration = { - bankTokens: parseBankTokens(process.env.FAUCET_TOKENS || "COSM=10^6ucosm, STAKE=10^6ustake"), + bankTokens: parseBankTokens(process.env.FAUCET_TOKENS || "ucosm, ustake"), }; diff --git a/packages/faucet/src/debugging.ts b/packages/faucet/src/debugging.ts index b54bae267f..85063d7cdf 100644 --- a/packages/faucet/src/debugging.ts +++ b/packages/faucet/src/debugging.ts @@ -1,38 +1,35 @@ import { Coin } from "@cosmjs/launchpad"; -import { TokenConfiguration } from "./tokenmanager"; import { MinimalAccount, SendJob } from "./types"; /** A string representation of a coin in a human-readable format that can change at any time */ -function debugCoin(coin: Coin, tokens: TokenConfiguration): string { - const meta = tokens.bankTokens.find((token) => token.denom == coin.denom); - if (!meta) throw new Error(`No token configuration found for denom ${coin.denom}`); - return `${coin.amount} ${meta?.denom}`; +function debugCoin(coin: Coin): string { + return `${coin.amount} ${coin.denom}`; } /** A string representation of a balance in a human-readable format that can change at any time */ -export function debugBalance(data: readonly Coin[], tokens: TokenConfiguration): string { - return `[${data.map((b) => debugCoin(b, tokens)).join(", ")}]`; +export function debugBalance(data: readonly Coin[]): string { + return `[${data.map((b) => debugCoin(b)).join(", ")}]`; } /** A string representation of an account in a human-readable format that can change at any time */ -export function debugAccount(account: MinimalAccount, tokens: TokenConfiguration): string { - return `${account.address}: ${debugBalance(account.balance, tokens)}`; +export function debugAccount(account: MinimalAccount): string { + return `${account.address}: ${debugBalance(account.balance)}`; } -export function logAccountsState(accounts: readonly MinimalAccount[], tokens: TokenConfiguration): void { +export function logAccountsState(accounts: readonly MinimalAccount[]): void { if (accounts.length < 2) { throw new Error("List of accounts must contain at least one token holder and one distributor"); } const holder = accounts[0]; const distributors = accounts.slice(1); - console.info("Holder:\n" + ` ${debugAccount(holder, tokens)}`); - console.info("Distributors:\n" + distributors.map((r) => ` ${debugAccount(r, tokens)}`).join("\n")); + console.info("Holder:\n" + ` ${debugAccount(holder)}`); + console.info("Distributors:\n" + distributors.map((r) => ` ${debugAccount(r)}`).join("\n")); } -export function logSendJob(job: SendJob, tokens: TokenConfiguration): void { +export function logSendJob(job: SendJob): void { const from = job.sender; const to = job.recipient; - const amount = debugCoin(job.amount, tokens); + const amount = debugCoin(job.amount); console.info(`Sending ${amount} from ${from} to ${to} ...`); } diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts index f35b72fa76..20558f0086 100644 --- a/packages/faucet/src/faucet.spec.ts +++ b/packages/faucet/src/faucet.spec.ts @@ -14,18 +14,7 @@ function pendingWithoutWasmd(): void { const httpUrl = "http://localhost:1317"; const defaultTokenConfig: TokenConfiguration = { - bankTokens: [ - { - fractionalDigits: 6, - tickerSymbol: "COSM", - denom: "ucosm", - }, - { - fractionalDigits: 6, - tickerSymbol: "STAKE", - denom: "ustake", - }, - ], + bankTokens: ["ucosm", "ustake"], }; const defaultAddressPrefix = "cosmos"; @@ -57,10 +46,7 @@ describe("Faucet", () => { pendingWithoutWasmd(); const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3); const tickers = await faucet.availableTokens(); - expect(tickers).toEqual([ - { denom: "ucosm", tickerSymbol: "COSM", fractionalDigits: 6 }, - { denom: "ustake", tickerSymbol: "STAKE", fractionalDigits: 6 }, - ]); + expect(tickers).toEqual(["ucosm", "ustake"]); }); }); @@ -147,12 +133,12 @@ describe("Faucet", () => { }); }); - describe("loadTokenTickers", () => { + describe("configuredTokens", () => { it("works", async () => { pendingWithoutWasmd(); const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3); - const tickers = faucet.loadTokenTickers(); - expect(tickers).toEqual(["COSM", "STAKE"]); + const tickers = faucet.configuredTokens(); + expect(tickers).toEqual(["ucosm", "ustake"]); }); }); diff --git a/packages/faucet/src/faucet.ts b/packages/faucet/src/faucet.ts index f4521010b0..d6a8846a79 100644 --- a/packages/faucet/src/faucet.ts +++ b/packages/faucet/src/faucet.ts @@ -10,7 +10,6 @@ import * as constants from "./constants"; import { debugAccount, logAccountsState, logSendJob } from "./debugging"; import { createWallets } from "./profile"; import { TokenConfiguration, TokenManager } from "./tokenmanager"; -import { BankTokenMeta } from "./tokens"; import { MinimalAccount, SendJob } from "./types"; function isDefined(value: X | undefined): value is X { @@ -73,15 +72,15 @@ export class Faucet { } /** - * Returns a list of ticker symbols of tokens owned by the the holder and configured in the faucet + * Returns a list of denoms of tokens owned by the the holder and configured in the faucet */ - public async availableTokens(): Promise { + public async availableTokens(): Promise { const holderAccount = await this.readOnlyClient.getAccount(this.holderAddress); const balance = holderAccount ? holderAccount.balance : []; return balance .filter((b) => b.amount !== "0") - .map((b) => this.tokenConfig.bankTokens.find((token) => token.denom == b.denom)) + .map((b) => this.tokenConfig.bankTokens.find((token) => token == b.denom)) .filter(isDefined); } @@ -103,12 +102,13 @@ export class Faucet { recipient: recipient, amount: this.tokenManager.creditAmount(denom), }; - if (this.logging) logSendJob(job, this.tokenConfig); + if (this.logging) logSendJob(job); await this.send(job); } - public loadTokenTickers(): readonly string[] { - return this.tokenConfig.bankTokens.map((token) => token.tickerSymbol); + /** Returns a list to token denoms which are configured */ + public configuredTokens(): string[] { + return Array.from(this.tokenConfig.bankTokens); } public async loadAccounts(): Promise { @@ -134,14 +134,14 @@ export class Faucet { public async refill(): Promise { if (this.logging) { console.info(`Connected to network: ${await this.readOnlyClient.getChainId()}`); - console.info(`Tokens on network: ${this.loadTokenTickers().join(", ")}`); + console.info(`Tokens on network: ${this.configuredTokens().join(", ")}`); } const accounts = await this.loadAccounts(); - if (this.logging) logAccountsState(accounts, this.tokenConfig); + if (this.logging) logAccountsState(accounts); const [_, ...distributorAccounts] = accounts; - const availableTokenDenoms = (await this.availableTokens()).map((token) => token.denom); + const availableTokenDenoms = await this.availableTokens(); if (this.logging) console.info("Available tokens:", availableTokenDenoms); const jobs: SendJob[] = []; @@ -154,7 +154,7 @@ export class Faucet { console.info(`Refilling ${denom} of:`); console.info( refillDistibutors.length - ? refillDistibutors.map((r) => ` ${debugAccount(r, this.tokenConfig)}`).join("\n") + ? refillDistibutors.map((r) => ` ${debugAccount(r)}`).join("\n") : " none", ); } @@ -168,7 +168,7 @@ export class Faucet { } if (jobs.length > 0) { for (const job of jobs) { - if (this.logging) logSendJob(job, this.tokenConfig); + if (this.logging) logSendJob(job); // don't crash faucet when one send fails try { await this.send(job); @@ -180,7 +180,7 @@ export class Faucet { if (this.logging) { console.info("Done refilling accounts."); - logAccountsState(await this.loadAccounts(), this.tokenConfig); + logAccountsState(await this.loadAccounts()); } } else { if (this.logging) { diff --git a/packages/faucet/src/tokenmanager.spec.ts b/packages/faucet/src/tokenmanager.spec.ts index 52d05aa240..dbdcb65028 100644 --- a/packages/faucet/src/tokenmanager.spec.ts +++ b/packages/faucet/src/tokenmanager.spec.ts @@ -2,18 +2,7 @@ import { TokenConfiguration, TokenManager } from "./tokenmanager"; import { MinimalAccount } from "./types"; const dummyConfig: TokenConfiguration = { - bankTokens: [ - { - tickerSymbol: "TOKENZ", - fractionalDigits: 6, - denom: "utokenz", - }, - { - tickerSymbol: "TRASH", - fractionalDigits: 3, - denom: "mtrash", - }, - ], + bankTokens: ["utokenz", "mtrash"], }; describe("TokenManager", () => { diff --git a/packages/faucet/src/tokenmanager.ts b/packages/faucet/src/tokenmanager.ts index c15a785e5c..b8e249a4ce 100644 --- a/packages/faucet/src/tokenmanager.ts +++ b/packages/faucet/src/tokenmanager.ts @@ -1,7 +1,6 @@ import { Coin } from "@cosmjs/launchpad"; import { Decimal, Uint53 } from "@cosmjs/math"; -import { BankTokenMeta } from "./tokens"; import { MinimalAccount } from "./types"; const defaultCreditAmount = 10_000_000; @@ -14,7 +13,7 @@ const defaultRefillThresholdFactor = 8; export interface TokenConfiguration { /** Supported tokens of the Cosmos SDK bank module */ - readonly bankTokens: readonly BankTokenMeta[]; + readonly bankTokens: readonly string[]; } export class TokenManager { @@ -29,11 +28,9 @@ export class TokenManager { const amountFromEnv = process.env[`FAUCET_CREDIT_AMOUNT_${denom.toUpperCase()}`]; const amount = amountFromEnv ? Uint53.fromString(amountFromEnv).toNumber() : defaultCreditAmount; const value = new Uint53(amount * factor.toNumber()); - - const meta = this.getTokenMetaForDenom(denom); return { amount: value.toString(), - denom: meta.denom, + denom: denom, }; } @@ -51,26 +48,12 @@ export class TokenManager { /** true iff the distributor account needs a refill */ public needsRefill(account: MinimalAccount, denom: string): boolean { - const meta = this.getTokenMetaForDenom(denom); + const balanceAmount = account.balance.find((b) => b.denom === denom); - const balanceAmount = account.balance.find((b) => b.denom === meta.denom); - - const balance = Decimal.fromAtomics(balanceAmount ? balanceAmount.amount : "0", meta.fractionalDigits); + const balance = Decimal.fromAtomics(balanceAmount ? balanceAmount.amount : "0", 0); const thresholdAmount = this.refillThreshold(denom); - const threshold = Decimal.fromAtomics(thresholdAmount.amount, meta.fractionalDigits); + const threshold = Decimal.fromAtomics(thresholdAmount.amount, 0); return balance.isLessThan(threshold); } - - private getTokenMetaForDenom(denom: string): BankTokenMeta { - const match = this.config.bankTokens.find((token) => token.denom === denom); - if (!match) throw new Error(`No token found for denom: ${denom}`); - return match; - } - - private getTokenMetaForTicker(ticker: string): BankTokenMeta { - const match = this.config.bankTokens.find((token) => token.tickerSymbol === ticker); - if (!match) throw new Error(`No token found for ticker: ${ticker}`); - return match; - } } diff --git a/packages/faucet/src/tokens.spec.ts b/packages/faucet/src/tokens.spec.ts index 39836c9e10..d2b1421cc8 100644 --- a/packages/faucet/src/tokens.spec.ts +++ b/packages/faucet/src/tokens.spec.ts @@ -3,76 +3,29 @@ import { parseBankToken, parseBankTokens } from "./tokens"; describe("tokens", () => { describe("parseBankToken", () => { it("works", () => { - expect(parseBankToken("COSM=10^6ucosm")).toEqual({ - tickerSymbol: "COSM", - fractionalDigits: 6, - denom: "ucosm", - }); + expect(parseBankToken("ucosm")).toEqual("ucosm"); }); it("allows using whitespace", () => { - expect(parseBankToken("COSM = 10^6 ucosm")).toEqual({ - tickerSymbol: "COSM", - fractionalDigits: 6, - denom: "ucosm", - }); + expect(parseBankToken(" ucosm\n")).toEqual("ucosm"); }); }); describe("parseBankTokens", () => { it("works for one", () => { - expect(parseBankTokens("COSM=10^6ucosm")).toEqual([ - { - tickerSymbol: "COSM", - fractionalDigits: 6, - denom: "ucosm", - }, - ]); + expect(parseBankTokens("ucosm")).toEqual(["ucosm"]); }); it("works for two", () => { - expect(parseBankTokens("COSM=10^6ucosm,STAKE=10^3mstake")).toEqual([ - { - tickerSymbol: "COSM", - fractionalDigits: 6, - denom: "ucosm", - }, - { - tickerSymbol: "STAKE", - fractionalDigits: 3, - denom: "mstake", - }, - ]); + expect(parseBankTokens("ucosm,mstake")).toEqual(["ucosm", "mstake"]); }); it("ignores whitespace", () => { - expect(parseBankTokens("COSM=10^6ucosm, STAKE=10^3mstake\n")).toEqual([ - { - tickerSymbol: "COSM", - fractionalDigits: 6, - denom: "ucosm", - }, - { - tickerSymbol: "STAKE", - fractionalDigits: 3, - denom: "mstake", - }, - ]); + expect(parseBankTokens("ucosm, mstake\n")).toEqual(["ucosm", "mstake"]); }); it("ignores empty elements", () => { - expect(parseBankTokens("COSM=10^6ucosm,STAKE=10^3mstake,")).toEqual([ - { - tickerSymbol: "COSM", - fractionalDigits: 6, - denom: "ucosm", - }, - { - tickerSymbol: "STAKE", - fractionalDigits: 3, - denom: "mstake", - }, - ]); + expect(parseBankTokens("ucosm,mstake,")).toEqual(["ucosm", "mstake"]); }); }); }); diff --git a/packages/faucet/src/tokens.ts b/packages/faucet/src/tokens.ts index a3ca321bcb..0f4e8f5bdc 100644 --- a/packages/faucet/src/tokens.ts +++ b/packages/faucet/src/tokens.ts @@ -1,38 +1,14 @@ -import { Uint53 } from "@cosmjs/math"; +const parseBankTokenPattern = /^([a-zA-Z]{2,20})$/; -export interface BankTokenMeta { - readonly denom: string; - /** - * The token ticker symbol, e.g. ATOM or ETH. - */ - readonly tickerSymbol: string; - /** - * The number of fractional digits the token supports. - * - * A quantity is expressed as atomic units. 10^fractionalDigits of those - * atomic units make up 1 token. - * - * E.g. in Ethereum 10^18 wei are 1 ETH and from the quantity 123000000000000000000 - * the last 18 digits are the fractional part and the rest the wole part. - */ - readonly fractionalDigits: number; -} - -const parseBankTokenPattern = /^([a-zA-Z]{2,20})=10\^([0-9]+)([a-zA-Z]{2,20})$/; - -export function parseBankToken(input: string): BankTokenMeta { +export function parseBankToken(input: string): string { const match = input.replace(/\s/g, "").match(parseBankTokenPattern); if (!match) { throw new Error("Token could not be parsed. Format: {DISPLAY}=10^{DIGITS}{base}, e.g. ATOM=10^6uatom"); } - return { - tickerSymbol: match[1], - fractionalDigits: Uint53.fromString(match[2]).toNumber(), - denom: match[3], - }; + return match[1]; } -export function parseBankTokens(input: string): BankTokenMeta[] { +export function parseBankTokens(input: string): string[] { return input .trim() .split(",")