From cb55d2eacb09634ade6dc26aabd815b321db9f52 Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Fri, 8 Oct 2021 15:14:56 -0400 Subject: [PATCH] feat: add starting balance to faucet wallet (#1702) * refactor: add starting balance to faucet wallet and change `generateFaucetWallet()` to `fundWallet()` --- src/client/index.ts | 4 +- ...{generateFaucetWallet.ts => fundWallet.ts} | 58 +++++++++++-------- ...{generateFaucetWallet.ts => fundWallet.ts} | 19 +++--- test/integration/index.ts | 2 +- ...{generateFaucetWallet.ts => fundWallet.ts} | 2 +- 5 files changed, 49 insertions(+), 36 deletions(-) rename src/wallet/{generateFaucetWallet.ts => fundWallet.ts} (86%) rename test/integration/{generateFaucetWallet.ts => fundWallet.ts} (72%) rename test/wallet/{generateFaucetWallet.ts => fundWallet.ts} (94%) diff --git a/src/client/index.ts b/src/client/index.ts index 1864b2ed..dd562a41 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -97,7 +97,7 @@ import { submitSignedReliable, } from '../sugar/submit' import { ensureClassicAddress } from '../sugar/utils' -import generateFaucetWallet from '../wallet/generateFaucetWallet' +import fundWallet from '../wallet/fundWallet' import { Connection, @@ -545,7 +545,7 @@ class Client extends EventEmitter { public getBalances = getBalances public getOrderbook = getOrderbook - public generateFaucetWallet = generateFaucetWallet + public fundWallet = fundWallet } export { Client } diff --git a/src/wallet/generateFaucetWallet.ts b/src/wallet/fundWallet.ts similarity index 86% rename from src/wallet/generateFaucetWallet.ts rename to src/wallet/fundWallet.ts index d5a8105f..fc320e54 100644 --- a/src/wallet/generateFaucetWallet.ts +++ b/src/wallet/fundWallet.ts @@ -15,6 +15,11 @@ interface FaucetWallet { balance: number } +interface WalletWithStartingBalance { + wallet: Wallet + balance: number +} + enum FaucetNetwork { Testnet = 'faucet.altnet.rippletest.net', Devnet = 'faucet.devnet.rippletest.net', @@ -33,16 +38,16 @@ const MAX_ATTEMPTS = 20 * @returns A Wallet on the Testnet or Devnet that contains some amount of XRP. * @throws When either Client isn't connected or unable to fund wallet address. */ -async function generateFaucetWallet( +async function fundWallet( this: Client, wallet?: Wallet, -): Promise { +): Promise { if (!this.isConnected()) { throw new RippledError('Client not connected, cannot call faucet') } // Generate a new Wallet if no existing Wallet is provided or its address is invalid to fund - const fundWallet = + const walletToFund = wallet && isValidClassicAddress(wallet.classicAddress) ? wallet : Wallet.generate() @@ -51,7 +56,7 @@ async function generateFaucetWallet( const postBody = Buffer.from( new TextEncoder().encode( JSON.stringify({ - destination: fundWallet.classicAddress, + destination: walletToFund.classicAddress, }), ), ) @@ -59,7 +64,7 @@ async function generateFaucetWallet( let startingBalance = 0 try { startingBalance = Number( - await getAddressXrpBalance(this, fundWallet.classicAddress), + await getAddressXrpBalance(this, walletToFund.classicAddress), ) } catch { /* startingBalance remains '0' */ @@ -68,7 +73,7 @@ async function generateFaucetWallet( // Options to pass to https.request const options = getOptions(this, postBody) - return returnPromise(options, this, startingBalance, fundWallet, postBody) + return returnPromise(options, this, startingBalance, walletToFund, postBody) } // eslint-disable-next-line max-params -- Helper function created for organizational purposes @@ -76,9 +81,9 @@ async function returnPromise( options: RequestOptions, client: Client, startingBalance: number, - fundWallet: Wallet, + walletToFund: Wallet, postBody: Buffer, -): Promise { +): Promise { return new Promise((resolve, reject) => { const request = httpsRequest(options, (response) => { const chunks: Uint8Array[] = [] @@ -90,7 +95,7 @@ async function returnPromise( chunks, client, startingBalance, - fundWallet, + walletToFund, resolve, reject, ), @@ -126,8 +131,8 @@ async function onEnd( chunks: Uint8Array[], client: Client, startingBalance: number, - fundWallet: Wallet, - resolve: (wallet: Wallet) => void, + walletToFund: Wallet, + resolve: (response: WalletWithStartingBalance) => void, reject: (err: ErrorConstructor | Error | unknown) => void, ): Promise { const body = Buffer.concat(chunks).toString() @@ -138,7 +143,7 @@ async function onEnd( client, body, startingBalance, - fundWallet, + walletToFund, resolve, reject, ) @@ -155,13 +160,13 @@ async function onEnd( } } -// eslint-disable-next-line max-params -- Only used as a helper function +// eslint-disable-next-line max-params, max-lines-per-function -- Only used as a helper function, lines inc due to added balance. async function processSuccessfulResponse( client: Client, body: string, startingBalance: number, - fundWallet: Wallet, - resolve: (wallet: Wallet) => void, + walletToFund: Wallet, + resolve: (response: WalletWithStartingBalance) => void, reject: (err: ErrorConstructor | Error | unknown) => void, ): Promise { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- We know this is safe and correct @@ -174,14 +179,21 @@ async function processSuccessfulResponse( } try { // Check at regular interval if the address is enabled on the XRPL and funded - const isFunded = await hasAddressBalanceIncreased( + const updatedBalance = await getUpdatedBalance( client, classicAddress, startingBalance, ) - if (isFunded) { - resolve(fundWallet) + if (updatedBalance > startingBalance) { + resolve({ + wallet: walletToFund, + balance: await getUpdatedBalance( + client, + walletToFund.getClassicAddress(), + startingBalance, + ), + }) } else { reject( new XRPLFaucetError( @@ -237,18 +249,18 @@ async function getAddressXrpBalance( * @param originalBalance - The initial balance before the funding. * @returns A Promise boolean. */ -async function hasAddressBalanceIncreased( +async function getUpdatedBalance( client: Client, address: string, originalBalance: number, -): Promise { +): Promise { return new Promise((resolve, reject) => { let attempts = MAX_ATTEMPTS // eslint-disable-next-line @typescript-eslint/no-misused-promises -- Not actually misused here, different resolve const interval = setInterval(async () => { if (attempts < 0) { clearInterval(interval) - resolve(false) + resolve(originalBalance) } else { attempts -= 1 } @@ -263,7 +275,7 @@ async function hasAddressBalanceIncreased( if (newBalance > originalBalance) { clearInterval(interval) - resolve(true) + resolve(newBalance) } } catch (err) { clearInterval(interval) @@ -302,7 +314,7 @@ function getFaucetUrl(client: Client): FaucetNetwork | undefined { throw new XRPLFaucetError('Faucet URL is not defined or inferrable.') } -export default generateFaucetWallet +export default fundWallet const _private = { FaucetNetwork, diff --git a/test/integration/generateFaucetWallet.ts b/test/integration/fundWallet.ts similarity index 72% rename from test/integration/generateFaucetWallet.ts rename to test/integration/fundWallet.ts index da5d83af..dd19f7ee 100644 --- a/test/integration/generateFaucetWallet.ts +++ b/test/integration/fundWallet.ts @@ -6,14 +6,14 @@ import { Client, isValidClassicAddress, isValidXAddress } from 'xrpl-local' // how long before each test case times out const TIMEOUT = 60000 // This test is reliant on external networks, and as such may be flaky. -describe('generateFaucetWallet', function () { +describe('fundWallet', function () { this.timeout(TIMEOUT) it('submit generates a testnet wallet', async function () { const api = new Client('wss://s.altnet.rippletest.net:51233') await api.connect() - const wallet = await api.generateFaucetWallet() + const { wallet, balance } = await api.fundWallet() assert.notEqual(wallet, undefined) assert(isValidClassicAddress(wallet.classicAddress)) @@ -23,16 +23,16 @@ describe('generateFaucetWallet', function () { command: 'account_info', account: wallet.classicAddress, }) - assert.equal(info.result.account_data.Balance, '1000000000') + assert.equal(info.result.account_data.Balance, balance) - await api.generateFaucetWallet(wallet) + const { balance: newBalance } = await api.fundWallet(wallet) const afterSent = await api.request({ command: 'account_info', account: wallet.classicAddress, }) - assert.equal(afterSent.result.account_data.Balance, '2000000000') + assert.equal(afterSent.result.account_data.Balance, newBalance) await api.disconnect() }) @@ -40,7 +40,7 @@ describe('generateFaucetWallet', function () { const api = new Client('wss://s.devnet.rippletest.net:51233') await api.connect() - const wallet = await api.generateFaucetWallet() + const { wallet, balance } = await api.fundWallet() assert.notEqual(wallet, undefined) assert(isValidClassicAddress(wallet.classicAddress)) @@ -50,15 +50,16 @@ describe('generateFaucetWallet', function () { command: 'account_info', account: wallet.classicAddress, }) - assert.equal(info.result.account_data.Balance, '1000000000') - await api.generateFaucetWallet(wallet) + assert.equal(info.result.account_data.Balance, balance) + + const { balance: newBalance } = await api.fundWallet(wallet) const afterSent = await api.request({ command: 'account_info', account: wallet.classicAddress, }) - assert.equal(afterSent.result.account_data.Balance, '2000000000') + assert.equal(afterSent.result.account_data.Balance, newBalance) await api.disconnect() }) diff --git a/test/integration/index.ts b/test/integration/index.ts index 0b86e1c7..892544b8 100644 --- a/test/integration/index.ts +++ b/test/integration/index.ts @@ -37,5 +37,5 @@ export * from './requests/submit' export * from './requests/tx' export * from './requests/utility' -export * from './generateFaucetWallet' +export * from './fundWallet' export * from './integration' diff --git a/test/wallet/generateFaucetWallet.ts b/test/wallet/fundWallet.ts similarity index 94% rename from test/wallet/generateFaucetWallet.ts rename to test/wallet/fundWallet.ts index 82b71761..68237b21 100644 --- a/test/wallet/generateFaucetWallet.ts +++ b/test/wallet/fundWallet.ts @@ -1,6 +1,6 @@ import { assert } from 'chai' -import { _private } from '../../src/wallet/generateFaucetWallet' +import { _private } from '../../src/wallet/fundWallet' import { setupClient, teardownClient } from '../setupClient' const { FaucetNetwork, getFaucetUrl } = _private