Skip to main content
This feature is experimental. Support for unauthenticated users and more currencies is coming soon.
Fiat Onramp Privy provides a useFiatOnramp hook in @privy-io/react-auth that starts a fiat onramp flow in the Privy modal. Your app can use this hook to let authenticated users buy crypto with supported fiat currencies, such as USD and EUR.

Access the hook

Import and initialize useFiatOnramp:
import {useFiatOnramp} from '@privy-io/react-auth';

const {fund} = useFiatOnramp();

Start a fiat onramp flow

Call fund with source currency options, and a destination wallet.
await fund({
  source: {
    assets: ['usd', 'eur']
  },
  destination: {
    asset: 'usdc',
    chain: 'eip155:8453',
    address: '<wallet_address>'
  }
});
destination.chain accepts a CAIP-2 identifier (for example, eip155:8453 for Base or solana:mainnet for Solana).

Parameters

fund accepts an object with the following fields:
ParameterTypeDescription
source.assetsSupportedFiatCurrency[]Optional. The list of fiat source currencies your app allows. Defaults to all supported currencies. When provided, must be non-empty.
source.defaultAssetSupportedFiatCurrencyOptional. The source currency selected when the flow opens. Falls back to the locale currency, then to the first item in source.assets.
destination.assetstringRequired. Destination crypto asset for this flow (for example, 'usdc').
destination.chain`${string}:${string}`Required. Destination chain in CAIP-2 format.
destination.addressstringRequired. Destination wallet address for purchased funds.
environment'sandbox' | 'production'Optional. Onramp environment for provider APIs.
defaultAmountstringOptional. Initial fiat amount displayed in the amount step.

Supported fiat currencies

SupportedFiatCurrency accepts any of the following lowercase ISO 4217 codes:
CodeCurrency
usdUS Dollar
eurEuro
gbpBritish Pound
mxnMexican Peso
brlBrazilian Real
cnyChinese Yuan
jpyJapanese Yen
inrIndian Rupee
cadCanadian Dollar
krwSouth Korean Won
audAustralian Dollar
idrIndonesian Rupiah
sarSaudi Riyal
tryTurkish Lira
chfSwiss Franc
twdNew Taiwan Dollar
sekSwedish Krona
ngnNigerian Naira
plnPolish Zloty
arsArgentine Peso
aedUAE Dirham
thbThai Baht
zarSouth African Rand
dkkDanish Krone
egpEgyptian Pound
myrMalaysian Ringgit
sgdSingapore Dollar
copColombian Peso
phpPhilippine Peso
clpChilean Peso
bdtBangladeshi Taka
vndVietnamese Dong
czkCzech Koruna
ilsIsraeli Shekel
hkdHong Kong Dollar
nzdNew Zealand Dollar
pkrPakistani Rupee
ronRomanian Leu
kztKazakhstani Tenge
nokNorwegian Krone
hufHungarian Forint
uahUkrainian Hryvnia
kwdKuwaiti Dinar
qarQatari Riyal
etbEthiopian Birr
madMoroccan Dirham
bgnBulgarian Lev
kesKenyan Shilling
nprNepalese Rupee

Return value

fund returns a Promise with one of the following statuses:
StatusMeaning
'submitted'The user completed the provider flow, then exited before final confirmation finished in Privy.
'confirmed'The flow reached provider confirmation, and the user completed the success step.

Error handling

fund rejects on invalid configuration or incomplete flows. Common error cases include:
  • source.assets is empty
  • another fiat onramp flow is already in progress
  • the user closes the flow before submitting a purchase
  • provider session or status requests fail
Your app should wrap calls in try/catch and show clear UI feedback.

Complete example

import {useState} from 'react';
import {useFiatOnramp} from '@privy-io/react-auth';

export const BuyUsdcButton = ({address}: {address: string}) => {
  const {fund} = useFiatOnramp();
  const [isLoading, setIsLoading] = useState(false);

  const onBuyUsdc = async () => {
    setIsLoading(true);

    try {
      const result = await fund({
        source: {
          assets: ['usd', 'eur', 'gbp'],
          defaultAsset: 'usd'
        },
        destination: {
          asset: 'usdc',
          chain: 'eip155:8453',
          address
        },
        environment: 'production',
        defaultAmount: '50'
      });

      if (result.status === 'confirmed') {
        // Update post-purchase UI immediately.
      }

      if (result.status === 'submitted') {
        // Show pending state while the provider finalizes the transaction.
      }
    } catch (error) {
      // Show retry UI or an error banner.
      console.error(error);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <button type="button" onClick={onBuyUsdc} disabled={isLoading}>
      {isLoading ? 'Starting onramp…' : 'Buy USDC'}
    </button>
  );
};

Use with deposit addresses

Apps that already use deposit addresses to receive funds on behalf of users can pass that address directly as the destination.address in the fund call. The onramp flow purchases the specified crypto asset and delivers it straight to the deposit address, letting your app credit the user’s account through your existing settlement logic.
const onFundDepositAddress = async () => {
  return await fund({
    source: {
      assets: ['usd']
    },
    destination: {
      address: depositAddress,
      asset: 'usdc',
      chain: 'eip155:8453'
    }
  });
};
The destination.address does not need to be the user’s own wallet. It can be any valid address your app controls, such as a per-user deposit address generated by your backend. Set destination.asset and destination.chain to match the crypto asset and network that the deposit address expects as input.