From 59d336e54aa1d56bd48fad6eefd5a6ed5fbf2c91 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 10:32:16 +0100 Subject: [PATCH] Pull out faucet class Closes #65 --- packages/faucet/package.json | 1 + packages/faucet/src/actions/start/start.ts | 14 +- packages/faucet/src/cashflow.spec.ts | 124 ---------------- packages/faucet/src/cashflow.ts | 64 --------- packages/faucet/src/codec.ts | 28 +--- packages/faucet/src/constants.ts | 19 +++ packages/faucet/src/faucet.spec.ts | 157 +++++++++++++++++++++ packages/faucet/src/faucet.ts | 124 ++++++++++++++++ packages/faucet/src/multichainhelpers.ts | 51 ------- 9 files changed, 313 insertions(+), 269 deletions(-) delete mode 100644 packages/faucet/src/cashflow.spec.ts delete mode 100644 packages/faucet/src/cashflow.ts create mode 100644 packages/faucet/src/faucet.spec.ts create mode 100644 packages/faucet/src/faucet.ts diff --git a/packages/faucet/package.json b/packages/faucet/package.json index af98acd7..09d31032 100644 --- a/packages/faucet/package.json +++ b/packages/faucet/package.json @@ -40,6 +40,7 @@ "@iov/crypto": "^2.0.0-alpha.7", "@iov/encoding": "^2.0.0-alpha.7", "@iov/keycontrol": "^2.0.0-alpha.7", + "@iov/utils": "^2.0.0-alpha.7", "@koa/cors": "^3.0.0", "axios": "^0.19.0", "fast-deep-equal": "^3.1.1", diff --git a/packages/faucet/src/actions/start/start.ts b/packages/faucet/src/actions/start/start.ts index 45cc23c6..13ade74d 100644 --- a/packages/faucet/src/actions/start/start.ts +++ b/packages/faucet/src/actions/start/start.ts @@ -3,16 +3,15 @@ import cors = require("@koa/cors"); import Koa from "koa"; import bodyParser from "koa-bodyparser"; -import { creditAmount, setFractionalDigits } from "../../cashflow"; -import { codecDefaultFractionalDigits, codecImplementation, establishConnection } from "../../codec"; +import { codecImplementation, establishConnection } from "../../codec"; import * as constants from "../../constants"; import { logAccountsState, logSendJob } from "../../debugging"; +import { Faucet } from "../../faucet"; import { availableTokensFromHolder, identitiesOfFirstWallet, loadAccounts, loadTokenTickers, - refill, send, } from "../../multichainhelpers"; import { setSecretAndCreateIdentities } from "../../profile"; @@ -48,7 +47,6 @@ export async function start(args: ReadonlyArray): Promise { const connectedChainId = connection.chainId(); console.info(`Connected to network: ${connectedChainId}`); - setFractionalDigits(codecDefaultFractionalDigits()); await setSecretAndCreateIdentities(profile, constants.mnemonic, connectedChainId); const chainTokens = await loadTokenTickers(connection); @@ -67,8 +65,10 @@ export async function start(args: ReadonlyArray): Promise { const distibutorIdentities = identitiesOfFirstWallet(profile).slice(1); - await refill(profile, connection); - setInterval(async () => refill(profile, connection), 60_000); // ever 60 seconds + const faucet = new Faucet(constants.tokenConfig); + + await faucet.refill(profile, connection); + setInterval(async () => faucet.refill(profile, connection), 60_000); // ever 60 seconds console.info("Creating webserver ..."); const api = new Koa(); @@ -127,7 +127,7 @@ export async function start(args: ReadonlyArray): Promise { const job: SendJob = { sender: sender, recipient: address, - amount: creditAmount(ticker), + amount: faucet.creditAmount(ticker), tokenTicker: ticker, }; logSendJob(job); diff --git a/packages/faucet/src/cashflow.spec.ts b/packages/faucet/src/cashflow.spec.ts deleted file mode 100644 index b90f6347..00000000 --- a/packages/faucet/src/cashflow.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { TokenTicker } from "@iov/bcp"; - -import { creditAmount, refillAmount, refillThreshold, setFractionalDigits } from "./cashflow"; - -describe("Cashflow", () => { - beforeAll(() => { - setFractionalDigits(3); - }); - - describe("creditAmount", () => { - it("returns '10' + '000' by default", () => { - expect(creditAmount("TOKENZ" as TokenTicker)).toEqual({ - quantity: "10000", - fractionalDigits: 3, - tokenTicker: "TOKENZ", - }); - expect(creditAmount("TRASH" as TokenTicker)).toEqual({ - quantity: "10000", - fractionalDigits: 3, - tokenTicker: "TRASH", - }); - }); - - it("returns value from env variable + '000' when set", () => { - process.env.FAUCET_CREDIT_AMOUNT_WTF = "22"; - expect(creditAmount("WTF" as TokenTicker)).toEqual({ - quantity: "22000", - fractionalDigits: 3, - tokenTicker: "WTF", - }); - }); - - it("returns default from env variable + '000' when set to empty", () => { - process.env.FAUCET_CREDIT_AMOUNT_WTF = ""; - expect(creditAmount("WTF" as TokenTicker)).toEqual({ - quantity: "10000", - fractionalDigits: 3, - tokenTicker: "WTF", - }); - }); - }); - - describe("refillAmount", () => { - beforeEach(() => { - process.env.FAUCET_REFILL_FACTOR = ""; - }); - it("returns 20*10 + '000' by default", () => { - expect(refillAmount("TOKENZ" as TokenTicker)).toEqual({ - quantity: "200000", - fractionalDigits: 3, - tokenTicker: "TOKENZ", - }); - }); - - it("returns 20*22 + '000' when credit amount is 22", () => { - process.env.FAUCET_CREDIT_AMOUNT_WTF = "22"; - expect(refillAmount("WTF" as TokenTicker)).toEqual({ - quantity: "440000", - fractionalDigits: 3, - tokenTicker: "WTF", - }); - }); - - it("returns 30*10 + '000' when refill factor is 30", () => { - process.env.FAUCET_REFILL_FACTOR = "30"; - expect(refillAmount("TOKENZ" as TokenTicker)).toEqual({ - quantity: "300000", - fractionalDigits: 3, - tokenTicker: "TOKENZ", - }); - }); - - it("returns 30*22 + '000' when refill factor is 30 and credit amount is 22", () => { - process.env.FAUCET_REFILL_FACTOR = "30"; - process.env.FAUCET_CREDIT_AMOUNT_WTF = "22"; - expect(refillAmount("WTF" as TokenTicker)).toEqual({ - quantity: "660000", - fractionalDigits: 3, - tokenTicker: "WTF", - }); - }); - }); - - describe("refillThreshold", () => { - beforeEach(() => { - process.env.FAUCET_REFILL_THRESHOLD = ""; - }); - it("returns 8*10 + '000' by default", () => { - expect(refillThreshold("TOKENZ" as TokenTicker)).toEqual({ - quantity: "80000", - fractionalDigits: 3, - tokenTicker: "TOKENZ", - }); - }); - - it("returns 8*22 + '000' when credit amount is 22", () => { - process.env.FAUCET_CREDIT_AMOUNT_WTF = "22"; - expect(refillThreshold("WTF" as TokenTicker)).toEqual({ - quantity: "176000", - fractionalDigits: 3, - tokenTicker: "WTF", - }); - }); - - it("returns 5*10 + '000' when refill threshold is 5", () => { - process.env.FAUCET_REFILL_THRESHOLD = "5"; - expect(refillThreshold("TOKENZ" as TokenTicker)).toEqual({ - quantity: "50000", - fractionalDigits: 3, - tokenTicker: "TOKENZ", - }); - }); - - it("returns 5*22 + '000' when refill threshold is 5 and credit amount is 22", () => { - process.env.FAUCET_REFILL_THRESHOLD = "5"; - process.env.FAUCET_CREDIT_AMOUNT_WTF = "22"; - expect(refillThreshold("WTF" as TokenTicker)).toEqual({ - quantity: "110000", - fractionalDigits: 3, - tokenTicker: "WTF", - }); - }); - }); -}); diff --git a/packages/faucet/src/cashflow.ts b/packages/faucet/src/cashflow.ts deleted file mode 100644 index e95116fe..00000000 --- a/packages/faucet/src/cashflow.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Account, Amount, TokenTicker } from "@iov/bcp"; -import { Decimal, Uint53 } from "@iov/encoding"; - -/** Send `factor` times credit amount on refilling */ -const defaultRefillFactor = 20; - -/** refill when balance gets below `factor` times credit amount */ -const defaultRefillThresholdFactor = 8; - -// Load this from connection? -let globalFractionalDigits: number | undefined; - -export function setFractionalDigits(input: number): void { - globalFractionalDigits = input; -} - -export function getFractionalDigits(): number { - if (globalFractionalDigits === undefined) { - throw new Error("Fractional digits not set"); - } - return globalFractionalDigits; -} - -/** The amount of tokens that will be sent to the user */ -export function creditAmount(token: TokenTicker, factor: Uint53 = new Uint53(1)): Amount { - const amountFromEnv = process.env[`FAUCET_CREDIT_AMOUNT_${token}`]; - const amount = amountFromEnv ? Uint53.fromString(amountFromEnv).toNumber() : 10; - const value = new Uint53(amount * factor.toNumber()); - - const fractionalDigits = getFractionalDigits(); - return { - quantity: value.toString() + "0".repeat(fractionalDigits), - fractionalDigits: fractionalDigits, - tokenTicker: token, - }; -} - -export function refillAmount(token: TokenTicker): Amount { - const factorFromEnv = Number.parseInt(process.env.FAUCET_REFILL_FACTOR || "0", 10) || undefined; - const factor = new Uint53(factorFromEnv || defaultRefillFactor); - return creditAmount(token, factor); -} - -export function refillThreshold(token: TokenTicker): Amount { - const factorFromEnv = Number.parseInt(process.env.FAUCET_REFILL_THRESHOLD || "0", 10) || undefined; - const factor = new Uint53(factorFromEnv || defaultRefillThresholdFactor); - return creditAmount(token, factor); -} - -/** true iff the distributor account needs a refill */ -export function needsRefill(account: Account, token: TokenTicker): boolean { - const balanceAmount = account.balance.find(b => b.tokenTicker === token); - - const balance = balanceAmount - ? Decimal.fromAtomics(balanceAmount.quantity, balanceAmount.fractionalDigits) - : Decimal.fromAtomics("0", 0); - - const thresholdAmount = refillThreshold(token); - const threshold = Decimal.fromAtomics(thresholdAmount.quantity, thresholdAmount.fractionalDigits); - - // TODO: perform < operation on Decimal type directly - // https://github.com/iov-one/iov-core/issues/1375 - return balance.toFloatApproximation() < threshold.toFloatApproximation(); -} diff --git a/packages/faucet/src/codec.ts b/packages/faucet/src/codec.ts index f5d27e48..cffbfd67 100644 --- a/packages/faucet/src/codec.ts +++ b/packages/faucet/src/codec.ts @@ -1,32 +1,14 @@ -import { CosmWasmCodec, CosmWasmConnection, TokenConfiguration } from "@cosmwasm/bcp"; +import { CosmWasmCodec, CosmWasmConnection } from "@cosmwasm/bcp"; import { TxCodec } from "@iov/bcp"; +import { tokenConfig } from "./constants"; + const prefix = "cosmos"; -const config: TokenConfiguration = { - bankTokens: [ - { - fractionalDigits: 6, - name: "Fee Token", - ticker: "COSM", - denom: "cosm", - }, - { - fractionalDigits: 6, - name: "Staking Token", - ticker: "STAKE", - denom: "stake", - }, - ], -}; export async function establishConnection(url: string): Promise { - return CosmWasmConnection.establish(url, prefix, config); + return CosmWasmConnection.establish(url, prefix, tokenConfig); } export function codecImplementation(): TxCodec { - return new CosmWasmCodec(prefix, config.bankTokens); -} - -export function codecDefaultFractionalDigits(): number { - return 6; + return new CosmWasmCodec(prefix, tokenConfig.bankTokens); } diff --git a/packages/faucet/src/constants.ts b/packages/faucet/src/constants.ts index 616d1acd..1faaa975 100644 --- a/packages/faucet/src/constants.ts +++ b/packages/faucet/src/constants.ts @@ -1,4 +1,23 @@ +import { TokenConfiguration } from "@cosmwasm/bcp"; + export const binaryName = "cosmwasm-faucet"; export const concurrency: number = Number.parseInt(process.env.FAUCET_CONCURRENCY || "", 10) || 5; export const port: number = Number.parseInt(process.env.FAUCET_PORT || "", 10) || 8000; export const mnemonic: string | undefined = process.env.FAUCET_MNEMONIC; + +export const tokenConfig: TokenConfiguration = { + bankTokens: [ + { + fractionalDigits: 6, + name: "Fee Token", + ticker: "COSM", + denom: "cosm", + }, + { + fractionalDigits: 6, + name: "Staking Token", + ticker: "STAKE", + denom: "stake", + }, + ], +}; diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts new file mode 100644 index 00000000..6d45e334 --- /dev/null +++ b/packages/faucet/src/faucet.spec.ts @@ -0,0 +1,157 @@ +import { TokenConfiguration } from "@cosmwasm/bcp"; +import { TokenTicker } from "@iov/bcp"; + +import { Faucet } from "./faucet"; + +const dummyConfig: TokenConfiguration = { + bankTokens: [ + { + ticker: "TOKENZ", + name: "The tokenz", + fractionalDigits: 6, + denom: "utokenz", + }, + { + ticker: "TRASH", + name: "Trash token", + fractionalDigits: 3, + denom: "mtrash", + }, + ], +}; + +describe("Faucet", () => { + describe("constructor", () => { + it("can be constructed", () => { + const faucet = new Faucet(dummyConfig); + expect(faucet).toBeTruthy(); + }); + }); + + describe("creditAmount", () => { + const faucet = new Faucet(dummyConfig); + + it("returns 10 tokens by default", () => { + expect(faucet.creditAmount("TOKENZ" as TokenTicker)).toEqual({ + quantity: "10000000", + fractionalDigits: 6, + tokenTicker: "TOKENZ", + }); + expect(faucet.creditAmount("TRASH" as TokenTicker)).toEqual({ + quantity: "10000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + + it("returns value from env variable when set", () => { + process.env.FAUCET_CREDIT_AMOUNT_TRASH = "22"; + expect(faucet.creditAmount("TRASH" as TokenTicker)).toEqual({ + quantity: "22000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + process.env.FAUCET_CREDIT_AMOUNT_TRASH = ""; + }); + + it("returns default when env variable is set to empty", () => { + process.env.FAUCET_CREDIT_AMOUNT_TRASH = ""; + expect(faucet.creditAmount("TRASH" as TokenTicker)).toEqual({ + quantity: "10000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + process.env.FAUCET_CREDIT_AMOUNT_TRASH = ""; + }); + }); + + describe("refillAmount", () => { + const faucet = new Faucet(dummyConfig); + + beforeEach(() => { + process.env.FAUCET_REFILL_FACTOR = ""; + process.env.FAUCET_CREDIT_AMOUNT_TRASH = ""; + }); + + it("returns 20*10 + '000' by default", () => { + expect(faucet.refillAmount("TRASH" as TokenTicker)).toEqual({ + quantity: "200000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + + it("returns 20*22 + '000' when credit amount is 22", () => { + process.env.FAUCET_CREDIT_AMOUNT_TRASH = "22"; + expect(faucet.refillAmount("TRASH" as TokenTicker)).toEqual({ + quantity: "440000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + + it("returns 30*10 + '000' when refill factor is 30", () => { + process.env.FAUCET_REFILL_FACTOR = "30"; + expect(faucet.refillAmount("TRASH" as TokenTicker)).toEqual({ + quantity: "300000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + + it("returns 30*22 + '000' when refill factor is 30 and credit amount is 22", () => { + process.env.FAUCET_REFILL_FACTOR = "30"; + process.env.FAUCET_CREDIT_AMOUNT_TRASH = "22"; + expect(faucet.refillAmount("TRASH" as TokenTicker)).toEqual({ + quantity: "660000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + }); + + describe("refillThreshold", () => { + const faucet = new Faucet(dummyConfig); + + beforeEach(() => { + process.env.FAUCET_REFILL_THRESHOLD = ""; + process.env.FAUCET_CREDIT_AMOUNT_TRASH = ""; + }); + + it("returns 8*10 + '000' by default", () => { + expect(faucet.refillThreshold("TRASH" as TokenTicker)).toEqual({ + quantity: "80000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + + it("returns 8*22 + '000' when credit amount is 22", () => { + process.env.FAUCET_CREDIT_AMOUNT_TRASH = "22"; + expect(faucet.refillThreshold("TRASH" as TokenTicker)).toEqual({ + quantity: "176000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + + it("returns 5*10 + '000' when refill threshold is 5", () => { + process.env.FAUCET_REFILL_THRESHOLD = "5"; + expect(faucet.refillThreshold("TRASH" as TokenTicker)).toEqual({ + quantity: "50000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + + it("returns 5*22 + '000' when refill threshold is 5 and credit amount is 22", () => { + process.env.FAUCET_REFILL_THRESHOLD = "5"; + process.env.FAUCET_CREDIT_AMOUNT_TRASH = "22"; + expect(faucet.refillThreshold("TRASH" as TokenTicker)).toEqual({ + quantity: "110000", + fractionalDigits: 3, + tokenTicker: "TRASH", + }); + }); + }); +}); diff --git a/packages/faucet/src/faucet.ts b/packages/faucet/src/faucet.ts new file mode 100644 index 00000000..b0427fe1 --- /dev/null +++ b/packages/faucet/src/faucet.ts @@ -0,0 +1,124 @@ +import { TokenConfiguration } from "@cosmwasm/bcp"; +import { Account, Amount, BlockchainConnection, TokenTicker } from "@iov/bcp"; +import { Decimal, Uint53 } from "@iov/encoding"; +import { UserProfile } from "@iov/keycontrol"; +import { sleep } from "@iov/utils"; + +import { debugAccount, logAccountsState, logSendJob } from "./debugging"; +import { + availableTokensFromHolder, + identitiesOfFirstWallet, + loadAccounts, + loadTokenTickers, + send, +} from "./multichainhelpers"; +import { SendJob } from "./types"; + +/** Send `factor` times credit amount on refilling */ +const defaultRefillFactor = 20; + +/** refill when balance gets below `factor` times credit amount */ +const defaultRefillThresholdFactor = 8; + +export class Faucet { + private readonly config: TokenConfiguration; + + public constructor(config: TokenConfiguration) { + this.config = config; + } + + /** The amount of tokens that will be sent to the user */ + public creditAmount(token: TokenTicker, factor: Uint53 = new Uint53(1)): Amount { + const amountFromEnv = process.env[`FAUCET_CREDIT_AMOUNT_${token}`]; + const amount = amountFromEnv ? Uint53.fromString(amountFromEnv).toNumber() : 10; + const value = new Uint53(amount * factor.toNumber()); + + const fractionalDigits = this.getFractionalDigits(token); + return { + quantity: value.toString() + "0".repeat(fractionalDigits), + fractionalDigits: fractionalDigits, + tokenTicker: token, + }; + } + + public refillAmount(token: TokenTicker): Amount { + const factorFromEnv = Number.parseInt(process.env.FAUCET_REFILL_FACTOR || "0", 10) || undefined; + const factor = new Uint53(factorFromEnv || defaultRefillFactor); + return this.creditAmount(token, factor); + } + + public refillThreshold(token: TokenTicker): Amount { + const factorFromEnv = Number.parseInt(process.env.FAUCET_REFILL_THRESHOLD || "0", 10) || undefined; + const factor = new Uint53(factorFromEnv || defaultRefillThresholdFactor); + return this.creditAmount(token, factor); + } + + public async refill(profile: UserProfile, connection: BlockchainConnection): Promise { + console.info(`Connected to network: ${connection.chainId()}`); + console.info(`Tokens on network: ${(await loadTokenTickers(connection)).join(", ")}`); + + const holderIdentity = identitiesOfFirstWallet(profile)[0]; + + const accounts = await loadAccounts(profile, connection); + logAccountsState(accounts); + const holderAccount = accounts[0]; + const distributorAccounts = accounts.slice(1); + + const availableTokens = availableTokensFromHolder(holderAccount); + console.info("Available tokens:", availableTokens); + + const jobs: SendJob[] = []; + + for (const token of availableTokens) { + const refillDistibutors = distributorAccounts.filter(account => this.needsRefill(account, token)); + console.info(`Refilling ${token} of:`); + console.info( + refillDistibutors.length ? refillDistibutors.map(r => ` ${debugAccount(r)}`).join("\n") : " none", + ); + for (const refillDistibutor of refillDistibutors) { + jobs.push({ + sender: holderIdentity, + recipient: refillDistibutor.address, + tokenTicker: token, + amount: this.refillAmount(token), + }); + } + } + if (jobs.length > 0) { + for (const job of jobs) { + logSendJob(job); + await send(profile, connection, job); + await sleep(50); + } + + console.info("Done refilling accounts."); + logAccountsState(await loadAccounts(profile, connection)); + } else { + console.info("Nothing to be done. Anyways, thanks for checking."); + } + } + + /** true iff the distributor account needs a refill */ + public needsRefill(account: Account, token: TokenTicker): boolean { + const balanceAmount = account.balance.find(b => b.tokenTicker === token); + + const balance = balanceAmount + ? Decimal.fromAtomics(balanceAmount.quantity, balanceAmount.fractionalDigits) + : Decimal.fromAtomics("0", 0); + + const thresholdAmount = this.refillThreshold(token); + const threshold = Decimal.fromAtomics(thresholdAmount.quantity, thresholdAmount.fractionalDigits); + + // TODO: perform < operation on Decimal type directly + // https://github.com/iov-one/iov-core/issues/1375 + return balance.toFloatApproximation() < threshold.toFloatApproximation(); + } + + private getFractionalDigits(ticker: TokenTicker): number { + const match = [...this.config.bankTokens, ...(this.config.erc20Tokens || [])].find( + token => token.ticker === ticker, + ); + if (!match) throw new Error(`No token found for ticker symbol: ${ticker}`); + return match.fractionalDigits; + } +} diff --git a/packages/faucet/src/multichainhelpers.ts b/packages/faucet/src/multichainhelpers.ts index 57e2e554..3628e627 100644 --- a/packages/faucet/src/multichainhelpers.ts +++ b/packages/faucet/src/multichainhelpers.ts @@ -9,15 +9,9 @@ import { } from "@iov/bcp"; import { UserProfile } from "@iov/keycontrol"; -import { needsRefill, refillAmount } from "./cashflow"; import { codecImplementation } from "./codec"; -import { debugAccount, logAccountsState, logSendJob } from "./debugging"; import { SendJob } from "./types"; -async function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - export function identitiesOfFirstWallet(profile: UserProfile): ReadonlyArray { const wallet = profile.wallets.value[0]; return profile.getIdentities(wallet.id); @@ -88,48 +82,3 @@ export async function send( export function availableTokensFromHolder(holderAccount: Account): ReadonlyArray { return holderAccount.balance.map(coin => coin.tokenTicker); } - -export async function refill(profile: UserProfile, connection: BlockchainConnection): Promise { - console.info(`Connected to network: ${connection.chainId()}`); - console.info(`Tokens on network: ${(await loadTokenTickers(connection)).join(", ")}`); - - const holderIdentity = identitiesOfFirstWallet(profile)[0]; - - const accounts = await loadAccounts(profile, connection); - logAccountsState(accounts); - const holderAccount = accounts[0]; - const distributorAccounts = accounts.slice(1); - - const availableTokens = availableTokensFromHolder(holderAccount); - console.info("Available tokens:", availableTokens); - - const jobs: SendJob[] = []; - - for (const token of availableTokens) { - const refillDistibutors = distributorAccounts.filter(account => needsRefill(account, token)); - console.info(`Refilling ${token} of:`); - console.info( - refillDistibutors.length ? refillDistibutors.map(r => ` ${debugAccount(r)}`).join("\n") : " none", - ); - for (const refillDistibutor of refillDistibutors) { - jobs.push({ - sender: holderIdentity, - recipient: refillDistibutor.address, - tokenTicker: token, - amount: refillAmount(token), - }); - } - } - if (jobs.length > 0) { - for (const job of jobs) { - logSendJob(job); - await send(profile, connection, job); - await sleep(50); - } - - console.info("Done refilling accounts."); - logAccountsState(await loadAccounts(profile, connection)); - } else { - console.info("Nothing to be done. Anyways, thanks for checking."); - } -}