Skip to content

refactor: use NetworkID in faucet calculations #2950

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 2, 2025
3 changes: 2 additions & 1 deletion packages/xrpl/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
## Unreleased

### Fixed
* `OracleSet` transaction accepts hexadecimal string values for `AssetPrice` field
* Fix `OracleSet` transaction to accept hexadecimal string values for `AssetPrice` field
* `TransactionStream` model includes `hash` field in APIv2
* `TransactionStream` model includes `close_time_iso` field only for APIv2
* Better faucet support

## 4.2.0 (2025-2-13)

Expand Down
31 changes: 18 additions & 13 deletions packages/xrpl/src/Wallet/defaultFaucets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ export enum FaucetNetwork {
Devnet = 'faucet.devnet.rippletest.net',
}

export const FaucetNetworkPaths: Record<string, string> = {
export const faucetNetworkPaths: Record<string, string> = {
[FaucetNetwork.Testnet]: '/accounts',
[FaucetNetwork.Devnet]: '/accounts',
}

export const faucetNetworkIDs: Map<number, FaucetNetwork> = new Map([
[1, FaucetNetwork.Testnet],
[2, FaucetNetwork.Devnet],
])

/**
* Get the faucet host based on the Client connection.
*
Expand All @@ -31,19 +36,19 @@ export const FaucetNetworkPaths: Record<string, string> = {
export function getFaucetHost(client: Client): FaucetNetwork | undefined {
const connectionUrl = client.url

// 'altnet' for Ripple Testnet server and 'testnet' for XRPL Labs Testnet server
if (connectionUrl.includes('altnet') || connectionUrl.includes('testnet')) {
return FaucetNetwork.Testnet
}

if (connectionUrl.includes('sidechain-net2')) {
if (client.networkID == null) {
throw new XRPLFaucetError(
'Cannot fund an account on an issuing chain. Accounts must be created via the bridge.',
'Cannot create faucet URL without networkID or the faucet_host information',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'Cannot create faucet URL without networkID or the faucet_host information',
'Cannot create faucet URL without networkID',

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you want to remove the extra clause? If someone provides the faucet_host, the function doesn't need the network ID.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will faucet_host information help? This method will throw an error if client.networkID == null

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but the error is more reasonable from a user perspective, who is never directly calling getFaucetHost but will only ever call fundWallet

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the relevance of faucet_host in the error message. Suppose a user invokes fundWallet method and then get back this message: Cannot create faucet URL without networkID or the faucet_host information.

They will need to go through XRPL documentation about faucet_host.

I don't feel too strongly about it. However, the error message is not intuitive.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

faucet_host is a parameter in fundWallet...?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but client.networkID is the deciding parameter in the success/error of this method. I'm a bit thrown off by the mention of faucet_host, that is all.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you read through all the options on fundWallet? If you're using a custom devnet and have set up your own faucet for that, this function won't work unless you specify faucetHost in the params.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you read through all the options on fundWallet?

I had not.

)
}

if (connectionUrl.includes('devnet')) {
return FaucetNetwork.Devnet
if (faucetNetworkIDs.has(client.networkID)) {
return faucetNetworkIDs.get(client.networkID)
}

// 'altnet' for Ripple Testnet server and 'testnet' for XRPL Labs Testnet server
if (connectionUrl.includes('altnet') || connectionUrl.includes('testnet')) {
return FaucetNetwork.Testnet
}

throw new XRPLFaucetError('Faucet URL is not defined or inferrable.')
Expand All @@ -54,11 +59,11 @@ export function getFaucetHost(client: Client): FaucetNetwork | undefined {
*
* @param hostname - hostname.
* @returns A String with the correct path for the input hostname.
* If hostname undefined or cannot find (key, value) pair in {@link FaucetNetworkPaths}, defaults to '/accounts'
* If hostname undefined or cannot find (key, value) pair in {@link faucetNetworkPaths}, defaults to '/accounts'
*/
export function getDefaultFaucetPath(hostname: string | undefined): string {
export function getFaucetPath(hostname: string | undefined): string {
if (hostname === undefined) {
return '/accounts'
}
return FaucetNetworkPaths[hostname] || '/accounts'
return faucetNetworkPaths[hostname] || '/accounts'
}
8 changes: 2 additions & 6 deletions packages/xrpl/src/Wallet/fundWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ import { isValidClassicAddress } from 'ripple-address-codec'
import type { Client } from '../client'
import { XRPLFaucetError } from '../errors'

import {
FaucetWallet,
getFaucetHost,
getDefaultFaucetPath,
} from './defaultFaucets'
import { FaucetWallet, getFaucetHost, getFaucetPath } from './defaultFaucets'

import { Wallet } from '.'

Expand Down Expand Up @@ -148,7 +144,7 @@ export async function requestFunding(
if (!hostname) {
throw new XRPLFaucetError('No faucet hostname could be derived')
}
const pathname = options.faucetPath ?? getDefaultFaucetPath(hostname)
const pathname = options.faucetPath ?? getFaucetPath(hostname)
const response = await fetch(`https://${hostname}${pathname}`, {
method: 'POST',
headers: {
Expand Down
10 changes: 5 additions & 5 deletions packages/xrpl/test/wallet/fundWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { assert } from 'chai'

import {
FaucetNetwork,
FaucetNetworkPaths,
faucetNetworkPaths,
getFaucetHost,
getDefaultFaucetPath,
getFaucetPath,
} from '../../src/Wallet/defaultFaucets'
import {
setupClient,
Expand Down Expand Up @@ -45,20 +45,20 @@ describe('Get Faucet host ', function () {
})

it('returns the correct faucetPath for Devnet host', function () {
const expectedFaucetPath = FaucetNetworkPaths[FaucetNetwork.Devnet]
const expectedFaucetPath = faucetNetworkPaths[FaucetNetwork.Devnet]
// @ts-expect-error Intentionally modifying private data for test
testContext.client.connection.url = FaucetNetwork.Devnet

assert.strictEqual(
getDefaultFaucetPath(getFaucetHost(testContext.client)),
getFaucetPath(getFaucetHost(testContext.client)),
expectedFaucetPath,
)
})

it('returns the correct faucetPath for undefined host', function () {
const expectedFaucetPath = '/accounts'

assert.strictEqual(getDefaultFaucetPath(undefined), expectedFaucetPath)
assert.strictEqual(getFaucetPath(undefined), expectedFaucetPath)
})

it('throws if not connected to a known faucet host', function () {
Expand Down
Loading