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
Method Description 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
CAIP-2 identifier for the source chain (e.g. eip155:1 for Ethereum).
Token contract address on the source chain.
CAIP-2 identifier for the destination chain (e.g. eip155:8453 for Base).
Token contract address on the destination chain.
Wallet address to receive the deposited funds.
Refund address on the source chain. If not provided, Privy resolves one automatically from the
user’s linked wallets or creates an embedded wallet.
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 );
}
The quote ID returned as DepositAddressQuote.id.
The quote creation timestamp returned as DepositAddressQuote.created_at.
Optional abort signal to cancel polling.
Optional polling interval override in milliseconds.
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 );
}
The deposit address order ID.
Optional abort signal to cancel polling.
Optional polling interval override in milliseconds.
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' });
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.