text: """Handles wallet generation from a faucet."""
  import asyncio
  from typing import Optional
  
  import httpx
  from typing_extensions import Final
  
  from xrpl.asyncio.account import get_balance, get_next_valid_seq_number
  from xrpl.asyncio.clients import Client, XRPLRequestFailureException
  from xrpl.constants import XRPLException
  from xrpl.wallet.main import Wallet
  
  _TEST_FAUCET_URL: Final[str] = "https://faucet.altnet.rippletest.net/accounts"
  _DEV_FAUCET_URL: Final[str] = "https://faucet.devnet.rippletest.net/accounts"
  
  _TIMEOUT_SECONDS: Final[int] = 40
  _LEDGER_CLOSE_TIME: Final[int] = 4
  
  
  class XRPLFaucetException(XRPLException):
      """Faucet generation exception."""
  
      pass
  
  
  async def generate_faucet_wallet(
      client: Client, wallet: Optional[Wallet] = None, debug: bool = False
  ) -> Wallet:
      """
      Generates a random wallet and funds it using the XRPL Testnet Faucet.
  
      Args:
          client: the network client used to make network calls.
          wallet: the wallet to fund. If omitted or `None`, a new wallet is created.
          debug: Whether to print debug information as it creates the wallet.
  
      Returns:
          A Wallet on the testnet that contains some amount of XRP.
  
      Raises:
          XRPLFaucetException: if an address could not be funded with the faucet.
          XRPLRequestFailureException: if a request to the ledger fails.
          requests.exceptions.HTTPError: if the request to the faucet fails.
  
      .. # noqa: DAR402 exception raised in private method
      """
      faucet_url = get_faucet_url(client.url)
      if wallet is None:
          wallet = Wallet.create()
  
      address = wallet.classic_address
      # The faucet *can* be flakey... by printing info about this it's easier to
      # understand if tests are actually failing, or if it was just a faucet failure.
      if debug:
          print("Attempting to fund address {}".format(address))
      # Balance prior to asking for more funds
      starting_balance = await _check_wallet_balance(address, client)
  
      # Ask the faucet to send funds to the given address
      await _request_funding(faucet_url, address)
      # Wait for the faucet to fund our account or until timeout
      # Waits one second checks if balance has changed
      # If balance doesn't change it will attempt again until _TIMEOUT_SECONDS
      is_funded = False
      for _ in range(_TIMEOUT_SECONDS):
          await asyncio.sleep(1)
          if not is_funded:  # faucet transaction hasn't been validated yet
              current_balance = await _check_wallet_balance(address, client)
              # If our current balance has changed, then the account has been funded
              if current_balance > starting_balance:
                  if debug:
                      print("Faucet fund successful.")
                  is_funded = True
          else:  # wallet has been funded, now the ledger needs to know the account exists
              next_seq_num = await _try_to_get_next_seq(address, client)
              if next_seq_num is not None:
                  wallet.sequence = next_seq_num
                  return wallet
  
      raise XRPLFaucetException(
          "Unable to fund address with faucet after waiting {} seconds".format(
              _TIMEOUT_SECONDS
          )
      )
  
  
  def get_faucet_url(url: str) -> str:
      """
      Returns the URL of the faucet that should be used, based on whether the URL is from
      a testnet or devnet client.
  
      Args:
          url: The URL that the client is using to access the ledger.
  
      Returns:
          The URL of the matching faucet.
  
      Raises:
          XRPLFaucetException: if the provided URL is not for the testnet or devnet.
      """
      if "dev" in url:  # devnet
          return _DEV_FAUCET_URL
      if "altnet" in url or "test" in url:  # testnet
          return _TEST_FAUCET_URL
      raise XRPLFaucetException(
          "Cannot fund an account with a client that is not on the testnet or devnet."
      )
  
  
  async def _check_wallet_balance(address: str, client: Client) -> int:
      try:
          return await get_balance(address, client)
      except XRPLRequestFailureException as e:
          if e.error == "actNotFound":  # transaction has not gone through
              return 0
          # some other error
          raise
  
  
  async def _request_funding(url: str, address: str) -> None:
      async with httpx.AsyncClient() as http_client:
          response = await http_client.post(url=url, json={"destination": address})
      if not response.status_code == httpx.codes.OK:
          response.raise_for_status()
  
  
  async def _try_to_get_next_seq(address: str, client: Client) -> Optional[int]:
      try:
          return await get_next_valid_seq_number(address, client)
      except XRPLRequestFailureException as e:
          if e.error == "actNotFound":
              # faucet gen has not fully gone through, try again
              return None
          # some other error
          raise
  