Skip to main content
This hook is experimental and its interface may change.

Prerequisites

Before using the deposit address hooks, enable the deposit address feature for your app in the Privy Dashboard. See here for full guidance.

Overview

The headless useDepositAddress hook from @privy-io/react-auth/hooks provides stateless helpers for building a fully custom deposit address UI. Unlike the modal-based useDepositAddress from @privy-io/react-auth, this hook does not render the Privy modal — your app controls the entire user experience.

Access the hook

import {useDepositAddress} from '@privy-io/react-auth/hooks';

const {getConfig, generateDepositAddress, getDeposit, waitForDeposit, waitForCompletion} =
  useDepositAddress();

Methods

MethodDescription
getConfigFetches supported currencies and chains
generateDepositAddressCreates a deposit quote with a unique deposit address
getDepositFetches enriched order details by order ID
waitForDepositPolls until funds are received at the deposit address
waitForCompletionPolls an order until it reaches a terminal status

Step 1: Load deposit configuration

Call getConfig to fetch the available currencies and chains:
const config = await getConfig();
The returned DepositConfig object contains:
  • currencies — an array of supported tokens with their chain availability
  • chains — a record keyed by caip2 with chain metadata

Config shape

type DepositConfig = {
  currencies: Array<{
    symbol: string;
    name: string;
    logoURI: string;
    chains: Array<{caip2: string; address: string; decimals: number}>;
  }>;
  chains: Record<
    string,
    {chainId: number; caip2: string; displayName: string; iconUrl: string; vmType: string}
  >;
};

Example config response

{
  "currencies": [
    {
      "symbol": "USDC",
      "name": "USD Coin",
      "logoURI": "https://assets.privy.io/tokens/usdc.png",
      "chains": [
        {
          "caip2": "eip155:8453",
          "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
          "decimals": 6
        },
        {
          "caip2": "eip155:1",
          "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          "decimals": 6
        }
      ]
    },
    {
      "symbol": "ETH",
      "name": "Ethereum",
      "logoURI": "https://assets.privy.io/tokens/eth.png",
      "chains": [
        {
          "caip2": "eip155:8453",
          "address": "0x0000000000000000000000000000000000000000",
          "decimals": 18
        },
        {
          "caip2": "eip155:1",
          "address": "0x0000000000000000000000000000000000000000",
          "decimals": 18
        }
      ]
    }
  ],
  "chains": {
    "eip155:8453": {
      "chainId": 8453,
      "caip2": "eip155:8453",
      "displayName": "Base",
      "iconUrl": "https://assets.privy.io/chains/base.png",
      "vmType": "evm"
    },
    "eip155:1": {
      "chainId": 1,
      "caip2": "eip155:1",
      "displayName": "Ethereum",
      "iconUrl": "https://assets.privy.io/chains/ethereum.png",
      "vmType": "evm"
    }
  }
}

Using the config

Use the config to build chain and currency selectors in your UI:
function getChainsForDisplay(config: DepositConfig) {
  return Object.values(config.chains).map((chain) => ({
    caip2: chain.caip2,
    displayName: chain.displayName,
    iconUrl: chain.iconUrl
  }));
}

function getCurrenciesForChain(config: DepositConfig, caip2: string) {
  return config.currencies
    .filter((currency) => currency.chains.some((c) => c.caip2 === caip2))
    .map((currency) => {
      const chainInfo = currency.chains.find((c) => c.caip2 === caip2)!;
      return {symbol: currency.symbol, name: currency.name, address: chainInfo.address};
    });
}

Step 2: Generate a deposit address

Call generateDepositAddress with the source and destination details:
const quote = await generateDepositAddress({
  sourceChain: 'eip155:1',
  sourceCurrency: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  destinationChain: 'eip155:8453',
  destinationCurrency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  destinationAddress: '0x1234...abcd'
});
The returned DepositAddressQuote includes id, deposit_address, indicative_rate, time_estimate_seconds, and created_at.

Parameters

sourceChain
string
required
CAIP-2 identifier for the source chain (e.g. eip155:1 for Ethereum).
sourceCurrency
string
required
Token contract address on the source chain.
destinationChain
string
required
CAIP-2 identifier for the destination chain (e.g. eip155:8453 for Base).
destinationCurrency
string
required
Token contract address on the destination chain.
destinationAddress
string
required
Wallet address to receive the deposited funds.
refundAddress
string
Refund address on the source chain. If not provided, Privy resolves one automatically from the user’s linked wallets or creates an embedded wallet.
slippageBps
number
Slippage tolerance in basis points. Uses the default for the route if not provided.

Step 3: Display the deposit address

Once you have the quote, display quote.deposit_address to the user. The user sends funds to this address from the source chain. You can also show quote.indicative_rate and quote.time_estimate_seconds to set expectations.

Step 4: Wait for the deposit

After the user sends funds, call waitForDeposit to poll until the deposit is detected:
const result = await waitForDeposit({
  depositAddressId: quote.id,
  quoteCreatedAt: quote.created_at
});

if (result.status === 'success') {
  console.log('Order detected:', result.order.id);
}
depositAddressId
string
required
The quote ID returned as DepositAddressQuote.id.
quoteCreatedAt
string
required
The quote creation timestamp returned as DepositAddressQuote.created_at.
signal
AbortSignal
Optional abort signal to cancel polling.
pollIntervalMs
number
Optional polling interval override in milliseconds.
timeoutMs
number
Optional polling timeout override in milliseconds.

Return type

Both waitForDeposit and waitForCompletion return a DepositAddressPollingResult:
type DepositAddressPollingResult =
  | {status: 'success'; order: DepositAddressOrder}
  | {status: 'aborted'; error?: Error}
  | {status: 'timeout'; error?: Error};

type DepositAddressOrder = {
  id: string;
  source_chain: string;
  source_currency: string;
  source_amount: string;
  depositor_address: string;
  destination_chain: string;
  destination_currency: string;
  destination_amount: string | null;
  status: 'executing' | 'completed' | 'refunded' | 'failed';
  tracking_url: string;
  created_at: string;
  updated_at: string;
};
When status is 'completed', destination_amount contains the delivered amount. For all other statuses, destination_amount is null.

Step 5: Wait for order completion

Once a deposit order is detected, poll until it reaches a terminal status (completed, refunded, or failed):
const finalResult = await waitForCompletion({
  orderId: result.order.id
});

if (finalResult.status === 'success' && finalResult.order.status === 'completed') {
  console.log('Deposit delivered:', finalResult.order.destination_amount);
}
orderId
string
required
The deposit address order ID.
signal
AbortSignal
Optional abort signal to cancel polling.
pollIntervalMs
number
Optional polling interval override in milliseconds.
timeoutMs
number
Optional polling timeout override in milliseconds.

Fetch order details

Call getDeposit to fetch the full DepositAddressOrder for a given order at any time:
const order = await getDeposit({orderId: 'order_abc123'});
orderId
string
required
The deposit address order ID (returned as order.id from waitForDeposit or waitForCompletion).
Returns a DepositAddressOrder with the order’s current status, source and destination details, amounts, and a tracking_url for external tracking.

Full example

import {useState} from 'react';
import {
  useDepositAddress,
  type DepositAddressQuote,
  type DepositConfig
} from '@privy-io/react-auth/hooks';

function getCurrenciesForChain(config: DepositConfig, caip2: string) {
  return config.currencies
    .filter((currency) => currency.chains.some((c) => c.caip2 === caip2))
    .map((currency) => {
      const chainInfo = currency.chains.find((c) => c.caip2 === caip2)!;
      return {symbol: currency.symbol, address: chainInfo.address};
    });
}

function CustomDepositFlow() {
  const {getConfig, generateDepositAddress, waitForDeposit, waitForCompletion} =
    useDepositAddress();

  const [config, setConfig] = useState<DepositConfig | null>(null);
  const [quote, setQuote] = useState<DepositAddressQuote | null>(null);
  const [status, setStatus] = useState<string>('idle');

  const handleLoadConfig = async () => {
    const result = await getConfig();
    setConfig(result);
  };

  const handleDeposit = async () => {
    const newQuote = await generateDepositAddress({
      sourceChain: 'eip155:1',
      sourceCurrency: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
      destinationChain: 'eip155:8453',
      destinationCurrency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
      destinationAddress: '0x1234...abcd'
    });
    setQuote(newQuote);
    setStatus('awaiting_deposit');

    const orderResult = await waitForDeposit({
      depositAddressId: newQuote.id,
      quoteCreatedAt: newQuote.created_at
    });
    setStatus('processing');

    const finalResult = await waitForCompletion({
      orderId: orderResult.order.id
    });
    setStatus(finalResult.order.status);
  };

  return (
    <div>
      {!config && <button onClick={handleLoadConfig}>Load config</button>}
      {config && !quote && (
        <div>
          <p>
            Available chains:{' '}
            {Object.values(config.chains)
              .map((c) => c.displayName)
              .join(', ')}
          </p>
          <button onClick={handleDeposit}>Start deposit</button>
        </div>
      )}
      {quote && (
        <div>
          <p>
            Send funds to: <code>{quote.deposit_address}</code>
          </p>
          <p>Estimated time: ~{Math.round(quote.time_estimate_seconds / 60)} min</p>
          <p>Status: {status}</p>
        </div>
      )}
    </div>
  );
}

Crypto deposit addresses

Use the modal-based deposit address flow.

Funding overview

All available methods for funding wallets.