From e41967fffcee46156e15f75d215498fe9a2e04a5 Mon Sep 17 00:00:00 2001 From: Caleb Kniffen Date: Wed, 26 Jul 2023 13:18:46 -0500 Subject: [PATCH] feat: remove 3 http related polyfills (#2375) Switch to using fetch for browser and `node-fetch` for node for the faucet calls. This reduces the webpack bundle by 3.2% or 16.5kb gzipped. The fundWallet code has been refactored to be much more straight forward due to not having to do such low level operations. This improves the frontend setup process by no longer requiring several polyfills such as `url`, `stream-http`, and `https-browserify`. --- UNIQUE_SETUPS.md | 15 +- package-lock.json | 76 +++-- package.json | 3 - packages/xrpl/package.json | 1 + packages/xrpl/src/Wallet/fundWallet.ts | 259 +++++++----------- packages/xrpl/src/client/BroadcastClient.ts | 2 +- packages/xrpl/src/index.ts | 1 + .../xrpl/test/integration/fundWallet.test.ts | 2 +- packages/xrpl/test/webpack.config.js | 3 - packages/xrpl/webpack.config.js | 3 - 10 files changed, 163 insertions(+), 202 deletions(-) diff --git a/UNIQUE_SETUPS.md b/UNIQUE_SETUPS.md index bc45269cc5..f383f925c0 100644 --- a/UNIQUE_SETUPS.md +++ b/UNIQUE_SETUPS.md @@ -22,12 +22,8 @@ To use `xrpl.js` with React, you need to install shims for core NodeJS modules. assert \ buffer \ crypto-browserify \ - https-browserify \ - os-browserify \ process \ - stream-browserify \ - stream-http \ - url + stream-browserify ``` 2. Modify your webpack configuration @@ -48,11 +44,7 @@ To use `xrpl.js` with React, you need to install shims for core NodeJS modules. Object.assign(fallback, { assert: require.resolve("assert"), crypto: require.resolve("crypto-browserify"), - http: require.resolve("stream-http"), - https: require.resolve("https-browserify"), - os: require.resolve("os-browserify"), stream: require.resolve("stream-browserify"), - url: require.resolve("url"), ws: require.resolve("xrpl/dist/npm/client/WSWrapper"), }); config.resolve.fallback = fallback; @@ -141,7 +133,6 @@ export default defineConfig({ }, optimizeDeps: { esbuildOptions: { - define: { global: 'globalThis', }, @@ -165,8 +156,6 @@ resolve: { events: 'events', crypto: 'crypto-browserify', stream: 'stream-browserify', - http: 'stream-http', - https: 'https-browserify', ws: 'xrpl/dist/npm/client/WSWrapper', }, }}) @@ -181,8 +170,6 @@ npm install --save-dev @esbuild-plugins/node-globals-polyfill \ events \ crypto-browserify \ stream-browserify \ - stream-http \ - https-browserify \ xrpl ``` diff --git a/package-lock.json b/package-lock.json index b9c5f87aba..337f40f88a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,6 @@ "eslint-plugin-tsdoc": "^0.2.16", "eventemitter2": "^6.0.0", "expect": "^29.3.1", - "https-browserify": "^1.0.0", "jest": "^29.3.1", "jest-mock": "^29.3.1", "lerna": "^4.0.0", @@ -55,12 +54,10 @@ "source-map-loader": "^4.0.1", "source-map-support": "^0.5.16", "stream-browserify": "^3.0.0", - "stream-http": "3.2.0", "ts-jest": "^29.0.3", "ts-loader": "^9.2.5", "ts-node": "^10.2.1", "typescript": "^5.1.6", - "url": "^0.11.0", "webpack": "^5.81.0", "webpack-bundle-analyzer": "^4.1.0", "webpack-cli": "^5.0.1" @@ -5333,6 +5330,33 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5864,7 +5888,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, "optional": true, "dependencies": { "iconv-lite": "^0.6.2" @@ -5874,7 +5897,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -10554,8 +10576,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", @@ -12920,7 +12941,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, "engines": { "node": ">=6" } @@ -13762,7 +13782,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "devOptional": true }, "node_modules/schema-utils": { "version": "3.3.0", @@ -14943,7 +14963,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, "dependencies": { "punycode": "^2.1.1" }, @@ -15633,7 +15652,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, "engines": { "node": ">=10.4" } @@ -15819,7 +15837,6 @@ "version": "8.7.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, "dependencies": { "lodash": "^4.7.0", "tr46": "^2.1.0", @@ -16264,6 +16281,7 @@ "bignumber.js": "^9.0.0", "bip32": "^2.0.6", "bip39": "^3.0.4", + "cross-fetch": "^4.0.0", "ripple-address-codec": "^4.3.0", "ripple-binary-codec": "^1.7.1", "ripple-keypairs": "^1.3.0", @@ -20581,6 +20599,24 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -21001,7 +21037,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, "optional": true, "requires": { "iconv-lite": "^0.6.2" @@ -21011,7 +21046,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -24663,8 +24697,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -26544,8 +26577,7 @@ "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, "pure-rand": { "version": "6.0.2", @@ -27180,7 +27212,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "devOptional": true }, "schema-utils": { "version": "3.3.0", @@ -28096,7 +28128,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, "requires": { "punycode": "^2.1.1" } @@ -28599,8 +28630,7 @@ "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" }, "webpack": { "version": "5.88.1", @@ -28716,7 +28746,6 @@ "version": "8.7.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, "requires": { "lodash": "^4.7.0", "tr46": "^2.1.0", @@ -28965,6 +28994,7 @@ "bip39": "^3.0.4", "browserify-fs": "^1.0.0", "constants-browserify": "^1.0.0", + "cross-fetch": "^4.0.0", "https-browserify": "^1.0.0", "https-proxy-agent": "^7.0.1", "karma": "^6.4.1", diff --git a/package.json b/package.json index ee49d30806..078df97b92 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "eslint-plugin-tsdoc": "^0.2.16", "eventemitter2": "^6.0.0", "expect": "^29.3.1", - "https-browserify": "^1.0.0", "jest": "^29.3.1", "jest-mock": "^29.3.1", "lerna": "^4.0.0", @@ -60,12 +59,10 @@ "source-map-loader": "^4.0.1", "source-map-support": "^0.5.16", "stream-browserify": "^3.0.0", - "stream-http": "3.2.0", "ts-jest": "^29.0.3", "ts-loader": "^9.2.5", "ts-node": "^10.2.1", "typescript": "^5.1.6", - "url": "^0.11.0", "webpack": "^5.81.0", "webpack-bundle-analyzer": "^4.1.0", "webpack-cli": "^5.0.1" diff --git a/packages/xrpl/package.json b/packages/xrpl/package.json index 3976df058b..60da5ee0e2 100644 --- a/packages/xrpl/package.json +++ b/packages/xrpl/package.json @@ -24,6 +24,7 @@ "bignumber.js": "^9.0.0", "bip32": "^2.0.6", "bip39": "^3.0.4", + "cross-fetch": "^4.0.0", "ripple-address-codec": "^4.3.0", "ripple-binary-codec": "^1.7.1", "ripple-keypairs": "^1.3.0", diff --git a/packages/xrpl/src/Wallet/fundWallet.ts b/packages/xrpl/src/Wallet/fundWallet.ts index c75b607ed6..6c7fd981f1 100644 --- a/packages/xrpl/src/Wallet/fundWallet.ts +++ b/packages/xrpl/src/Wallet/fundWallet.ts @@ -1,6 +1,4 @@ -import { IncomingMessage } from 'http' -import { request as httpsRequest, RequestOptions } from 'https' - +import fetch from 'cross-fetch' import { isValidClassicAddress } from 'ripple-address-codec' import type { Client } from '../client' @@ -19,6 +17,40 @@ const INTERVAL_SECONDS = 1 // Maximum attempts to retrieve a balance const MAX_ATTEMPTS = 20 +export interface FundingOptions { + /** + * A custom amount to fund, if undefined or null, the default amount will be 1000. + */ + amount?: string + /** + * A custom host for a faucet server. On devnet, testnet, AMM devnet, and HooksV3 testnet, `fundWallet` will + * attempt to determine the correct server automatically. In other environments, or if you would like to customize + * the faucet host in devnet or testnet, you should provide the host using this option. + */ + faucetHost?: string + /** + * A custom path for a faucet server. On devnet, + * testnet, AMM devnet, and HooksV3 testnet, `fundWallet` will + * attempt to determine the correct path automatically. In other environments, + * or if you would like to customize the faucet path in devnet or testnet, + * you should provide the path using this option. + * Ex: client.fundWallet(null,{'faucet.altnet.rippletest.net', '/accounts'}) + * specifies a request to 'faucet.altnet.rippletest.net/accounts' to fund a new wallet. + */ + faucetPath?: string + /** + * An optional field to indicate the use case context of the faucet transaction + * Ex: integration test, code snippets. + */ + usageContext?: string +} + +interface FaucetRequestBody { + destination?: string + xrpAmount?: string + usageContext?: string + userAgent: string +} /** * The fundWallet() method is used to send an amount of XRP (usually 1000) to a new (randomly generated) * or existing XRP Ledger wallet. @@ -65,36 +97,16 @@ const MAX_ATTEMPTS = 20 * * @param this - Client. * @param wallet - An existing XRPL Wallet to fund. If undefined or null, a new Wallet will be created. - * @param options - See below. - * @param options.faucetHost - A custom host for a faucet server. On devnet, - * testnet, AMM devnet, and HooksV3 testnet, `fundWallet` will - * attempt to determine the correct server automatically. In other environments, - * or if you would like to customize the faucet host in devnet or testnet, - * you should provide the host using this option. - * @param options.faucetPath - A custom path for a faucet server. On devnet, - * testnet, AMM devnet, and HooksV3 testnet, `fundWallet` will - * attempt to determine the correct path automatically. In other environments, - * or if you would like to customize the faucet path in devnet or testnet, - * you should provide the path using this option. - * Ex: client.fundWallet(null,{'faucet.altnet.rippletest.net', '/accounts'}) - * specifies a request to 'faucet.altnet.rippletest.net/accounts' to fund a new wallet. - * @param options.amount - A custom amount to fund, if undefined or null, the default amount will be 1000. - * @param options.usageContext - An optional field to indicate the use case context of the faucet transaction - * Ex: integration test, code snippets. + * @param options - FundingOptions + * @returns A Wallet on the Testnet or Devnet that contains some amount of XRP, * and that wallet's balance in XRP. * @throws When either Client isn't connected or unable to fund wallet address. */ -// eslint-disable-next-line max-lines-per-function -- All lines necessary async function fundWallet( this: Client, wallet?: Wallet | null, - options?: { - faucetHost?: string - faucetPath?: string - amount?: string - usageContext?: string - }, + options: FundingOptions = {}, ): Promise<{ wallet: Wallet balance: number @@ -102,6 +114,7 @@ async function fundWallet( if (!this.isConnected()) { throw new RippledError('Client not connected, cannot call faucet') } + const existingWallet = Boolean(wallet) // Generate a new Wallet if no existing Wallet is provided or its address is invalid to fund const walletToFund = @@ -110,151 +123,83 @@ async function fundWallet( : Wallet.generate() // Create the POST request body - const postBody = Buffer.from( - new TextEncoder().encode( - JSON.stringify({ - destination: walletToFund.classicAddress, - xrpAmount: options?.amount, - userAgent: 'xrpl.js', - usageContext: options?.usageContext, - }), - ), - ) + const postBody: FaucetRequestBody = { + destination: walletToFund.classicAddress, + xrpAmount: options.amount, + usageContext: options.usageContext, + userAgent: 'xrpl.js', + } let startingBalance = 0 - try { - startingBalance = Number( - await this.getXrpBalance(walletToFund.classicAddress), - ) - } catch { - /* startingBalance remains '0' */ + if (existingWallet) { + try { + startingBalance = Number( + await this.getXrpBalance(walletToFund.classicAddress), + ) + } catch { + /* startingBalance remains what it was previously */ + } } - // Options to pass to https.request - const httpOptions = getHTTPOptions(this, postBody, { - hostname: options?.faucetHost, - pathname: options?.faucetPath, - }) - return returnPromise( - httpOptions, - this, - startingBalance, - walletToFund, - postBody, - ) + return requestFunding(options, this, startingBalance, walletToFund, postBody) } // eslint-disable-next-line max-params -- Helper function created for organizational purposes -async function returnPromise( - options: RequestOptions, +async function requestFunding( + options: FundingOptions, client: Client, startingBalance: number, walletToFund: Wallet, - postBody: Buffer, + postBody: FaucetRequestBody, ): Promise<{ wallet: Wallet balance: number }> { - return new Promise((resolve, reject) => { - const request = httpsRequest(options, (response) => { - const chunks: Uint8Array[] = [] - response.on('data', (data) => chunks.push(data)) - // eslint-disable-next-line @typescript-eslint/no-misused-promises -- not actually misused, different resolve/reject - response.on('end', async () => - onEnd( - response, - chunks, - client, - startingBalance, - walletToFund, - resolve, - reject, - ), - ) - }) - // POST the body - request.write(postBody) - - request.on('error', (error) => { - reject(error) - }) - - request.end() - }) -} - -function getHTTPOptions( - client: Client, - postBody: Uint8Array, - options?: { - hostname?: string - pathname?: string - }, -): RequestOptions { - const finalHostname = options?.hostname ?? getFaucetHost(client) - const finalPathname = options?.pathname ?? getDefaultFaucetPath(finalHostname) - return { - hostname: finalHostname, - port: 443, - path: finalPathname, + const hostname = options.faucetHost ?? getFaucetHost(client) + if (!hostname) { + throw new XRPLFaucetError('No faucet hostname could be derived') + } + const pathname = options.faucetPath ?? getDefaultFaucetPath(hostname) + const response = await fetch(`https://${hostname}${pathname}`, { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Content-Length': postBody.length, }, - } -} - -// eslint-disable-next-line max-params -- Helper function created for organizational purposes -async function onEnd( - response: IncomingMessage, - chunks: Uint8Array[], - client: Client, - startingBalance: number, - walletToFund: Wallet, - resolve: (response: { wallet: Wallet; balance: number }) => void, - reject: (err: ErrorConstructor | Error | unknown) => void, -): Promise { - const body = Buffer.concat(chunks).toString() + body: JSON.stringify(postBody), + }) + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- It's a FaucetWallet + const body = (await response.json()) as FaucetWallet // "application/json; charset=utf-8" - if (response.headers['content-type']?.startsWith('application/json')) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- We know this is safe and correct - const faucetWallet: FaucetWallet = JSON.parse(body) - const classicAddress = faucetWallet.account.classicAddress - await processSuccessfulResponse( + if ( + response.ok && + response.headers.get('Content-Type')?.startsWith('application/json') + ) { + const classicAddress = body.account.classicAddress + return processSuccessfulResponse( client, classicAddress, walletToFund, startingBalance, - resolve, - reject, - ) - } else { - reject( - new XRPLFaucetError( - `Content type is not \`application/json\`: ${JSON.stringify({ - statusCode: response.statusCode, - contentType: response.headers['content-type'], - body, - })}`, - ), ) } + return processError(response) } -// eslint-disable-next-line max-params, max-lines-per-function -- Only used as a helper function, lines inc due to added balance. +// eslint-disable-next-line max-params -- Only used as a helper function, lines inc due to added balance. async function processSuccessfulResponse( client: Client, classicAddress: string | undefined, walletToFund: Wallet, startingBalance: number, - resolve: (response: { wallet: Wallet; balance: number }) => void, - reject: (err: ErrorConstructor | Error | unknown) => void, -): Promise { +): Promise<{ + wallet: Wallet + balance: number +}> { if (!classicAddress) { - reject(new XRPLFaucetError(`The faucet account is undefined`)) - return + return Promise.reject( + new XRPLFaucetError(`The faucet account is undefined`), + ) } try { // Check at regular interval if the address is enabled on the XRPL and funded @@ -265,31 +210,37 @@ async function processSuccessfulResponse( ) if (updatedBalance > startingBalance) { - resolve({ + return { wallet: walletToFund, - balance: await getUpdatedBalance( - client, - walletToFund.classicAddress, - startingBalance, - ), - }) - } else { - reject( - new XRPLFaucetError( - `Unable to fund address with faucet after waiting ${ - INTERVAL_SECONDS * MAX_ATTEMPTS - } seconds`, - ), - ) + balance: updatedBalance, + } } + throw new XRPLFaucetError( + `Unable to fund address with faucet after waiting ${ + INTERVAL_SECONDS * MAX_ATTEMPTS + } seconds`, + ) } catch (err) { if (err instanceof Error) { - reject(new XRPLFaucetError(err.message)) + throw new XRPLFaucetError(err.message) } - reject(err) + throw err } } +async function processError(response: Response): Promise { + return Promise.reject( + new XRPLFaucetError( + `Request failed: ${JSON.stringify({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- json response could be anything + body: (await response.json()) || {}, + contentType: response.headers.get('Content-Type'), + statusCode: response.status, + })}`, + ), + ) +} + /** * Check at regular interval if the address is enabled on the XRPL and funded. * diff --git a/packages/xrpl/src/client/BroadcastClient.ts b/packages/xrpl/src/client/BroadcastClient.ts index 11854c939e..6ba076c7f4 100644 --- a/packages/xrpl/src/client/BroadcastClient.ts +++ b/packages/xrpl/src/client/BroadcastClient.ts @@ -6,7 +6,7 @@ import { Client, ClientOptions } from '.' * @deprecated since version 2.2.0. * Will be deleted in version 3.0.0. * - * Currently this implementation does not provide better reliability. + * Currently, this implementation does not provide better reliability. * To get better reliability, implement reconnect/error handling logic * and choose a reliable endpoint. * diff --git a/packages/xrpl/src/index.ts b/packages/xrpl/src/index.ts index accedcc222..e4fb697c1f 100644 --- a/packages/xrpl/src/index.ts +++ b/packages/xrpl/src/index.ts @@ -11,6 +11,7 @@ export { default as ECDSA } from './ECDSA' export * from './errors' +export { default as fundWallet, FundingOptions } from './Wallet/fundWallet' export { Wallet } from './Wallet' export { keyToRFC1751Mnemonic, rfc1751MnemonicToKey } from './Wallet/rfc1751' diff --git a/packages/xrpl/test/integration/fundWallet.test.ts b/packages/xrpl/test/integration/fundWallet.test.ts index 9b757f51ec..5b88cb1bf3 100644 --- a/packages/xrpl/test/integration/fundWallet.test.ts +++ b/packages/xrpl/test/integration/fundWallet.test.ts @@ -43,8 +43,8 @@ async function generate_faucet_wallet_and_fund_again( account: wallet.classicAddress, }) - assert.equal(dropsToXrp(afterSent.result.account_data.Balance), newBalance) assert(newBalance > balance) + assert.equal(dropsToXrp(afterSent.result.account_data.Balance), newBalance) await api.disconnect() } diff --git a/packages/xrpl/test/webpack.config.js b/packages/xrpl/test/webpack.config.js index eefe6a0c57..07b2a9d28e 100644 --- a/packages/xrpl/test/webpack.config.js +++ b/packages/xrpl/test/webpack.config.js @@ -104,12 +104,9 @@ function webpackForTest(testFileName) { fs: require.resolve('browserify-fs'), buffer: require.resolve('buffer/'), assert: require.resolve('assert/'), - url: require.resolve('url/'), stream: require.resolve('stream-browserify'), crypto: require.resolve('crypto-browserify'), path: require.resolve('path-browserify'), - http: require.resolve('stream-http'), - https: require.resolve('https-browserify'), }, }, } diff --git a/packages/xrpl/webpack.config.js b/packages/xrpl/webpack.config.js index 02f74e7fa3..e3cffc2d6c 100644 --- a/packages/xrpl/webpack.config.js +++ b/packages/xrpl/webpack.config.js @@ -65,11 +65,8 @@ function getDefaultConfiguration() { fallback: { buffer: require.resolve('buffer/'), assert: require.resolve('assert/'), - url: require.resolve('url/'), stream: require.resolve('stream-browserify'), crypto: require.resolve('crypto-browserify'), - https: require.resolve('https-browserify'), - http: require.resolve('stream-http'), }, }, }