The Fund component provides a comprehensive solution for implementing cryptocurrency purchasing flows using fiat currency. It is designed to be highly customizable and composable, allowing developers to create tailored onramp experiences while maintaining a consistent and secure purchasing process.
The core features include:
The component is built using a composition pattern that allows for maximum flexibility while providing sensible defaults when customization is not needed.
The Fund component is composed of several subcomponents that work together to create the complete purchasing experience. This approach allows developers to customize the UI structure while maintaining the underlying functionality.
The main components are:
Fund: The root wrapper component that provides the FundContext and handles state managementFundTitle: Renders the title for the fund interface (defaults to "Deposit [cryptoCurrency]")FundForm: Manages the multi-view form interface for amount input + payment selection, error screens, and transaction statusFundFooter: The "Secured by Coinbase" footer componentThe Fund component accepts a children prop that can be either React nodes or a render function. When using a render function, it receives the current FundState as an argument, providing access to all state values without needing to use the useFundContext hook directly.
Example of children as a render function:
function MyFundPage() {
return (
<Fund
country="US"
subdivision="NY"
cryptoCurrency="eth"
fiatCurrency="usd"
fetchBuyQuote={fetchBuyQuote}
fetchBuyOptions={fetchBuyOptions}
network="base"
>
{(state) => (
<>
<h1>
{state.transactionStatus.statusName === "transactionSuccess" ? "Purchase Complete" : "Buy Crypto"}
</h1>
<FundTitle />
<FundForm />
<FundFooter />
</>
)}
</Fund>
);
}
The entire purchasing flow's state is managed by FundProvider and accessed via the useFundContext hook. This context contains:
country, subdivision: User's location for regulatory compliancecryptoCurrency, fiatCurrency: Selected currencies for the transactionnetwork: The blockchain network for the purchasecryptoAmount, fiatAmount: The amounts being purchased/spentselectedInputType: Whether the user is entering crypto or fiat amountsselectedPaymentMethod: The chosen payment methodexchangeRate: Current exchange rate between currenciestransactionStatus: Current status of the transaction (init, error, transactionSuccess, etc.)error: Any error that occurred during the processpaymentMethods: Available payment methods based on user locationThe FundForm component is the core of the purchasing interface, managing the display of different views based on the state. It handles three main views:
form: The main input interface for amount selection and payment methoderror: Error display when something goes wrongtransaction-status: Shows the status of an ongoing or completed transactionFundForm provides a children render prop that receives an object containing the current view and the Content component. This allows for wrapping the form content or injecting additional components based on the current state and view.
<FundForm>
{({ view, Content }) => (
<div className={`fund-view-${view}`}>
{view === "form" && <p>Enter amount and select payment method</p>}
{Content}
{view === "error" && <p>Contact support at help@example.com</p>}
</div>
)}
</FundForm>
The FundTitle component displays a contextual title for the fund interface. By default, it shows "Deposit [cryptoCurrency]" but can be customized by providing children. It accepts an as prop to render as different HTML elements (default is h2).
The Fund component monitors and reflects a complete transaction lifecycle with the following statuses:
init: Initial state, ready for user inputexit: User has exited the flowerror: An error occurred during the processtransactionSuccess: Transaction completed successfullytransactionPending: Transaction is being processedThese statuses can be monitored using the onStatus, onError, and onSuccess callbacks provided to the Fund component.
Implement a simple cryptocurrency purchase interface:
import {
FundModal,
type FetchBuyOptions,
type FetchBuyQuote,
} from '@coinbase/cdp-react';
function BuyCrypto() {
const fetchBuyQuote: FetchBuyQuote = async (params) => {
// Call your backend API to get a quote
const response = await fetch('/api/buy-quote', {
method: 'POST',
body: JSON.stringify(params),
});
return response.json();
};
const fetchBuyOptions: FetchBuyOptions = async (params) => {
// Call your backend API to get available payment methods
const response = await fetch('/api/buy-options', {
method: 'POST',
body: JSON.stringify(params),
});
return response.json();
};
return (
<FundModal
country="US"
subdivision="NY"
cryptoCurrency="eth"
fiatCurrency="usd"
fetchBuyQuote={fetchBuyQuote}
fetchBuyOptions={fetchBuyOptions}
network="base"
presetAmountInputs={[25, 50, 100]}
onSuccess={(data) => console.log('Purchase successful:', data)}
onError={(error) => console.error('Purchase failed:', error)}
/>
);
}
Customize the layout and add a page-level title:
import {
Fund,
FundFooter,
FundForm,
FundTitle,
useFundContext,
type FetchBuyOptions,
type FetchBuyQuote,
} from '@coinbase/cdp-react';
function CustomFundPage() {
const fetchBuyQuote: FetchBuyQuote = async (params) => {
// Call your backend API to get a quote
const response = await fetch('/api/buy-quote', {
method: 'POST',
body: JSON.stringify(params),
});
return response.json();
};
const fetchBuyOptions: FetchBuyOptions = async (params) => {
// Call your backend API to get available payment methods
const response = await fetch('/api/buy-options', {
method: 'POST',
body: JSON.stringify(params),
});
return response.json();
};
return (
<Fund
country="US"
subdivision="NY"
cryptoCurrency="btc"
fiatCurrency="usd"
fetchBuyQuote={fetchBuyQuote}
fetchBuyOptions={fetchBuyOptions}
network="base"
>
<CustomFundContent />
</Fund>
);
}
function CustomFundContent() {
const { state } = useFundContext();
const titleId = useId();
return (
<>
<div className="page-header">
<h1>Cryptocurrency Purchase Portal</h1>
<p>
Current rate: 1 {state.cryptoCurrency} =
{state.exchangeRate ? (1 / state.exchangeRate).toLocaleString() : "..."}{" "}
{state.fiatCurrency}
</p>
</div>
<FundTitle as="h2" id={titleId}>
Buy {state.cryptoCurrency}
</FundTitle>
<FundForm aria-labelledby={titleId}>
{({ view, Content }) => (
<>
{view === "form" && (
<p className="help-text">
Select an amount and payment method to continue
</p>
)}
{Content}
{view === "error" && (
<div className="error-help">
<p>Need help? Contact support@example.com</p>
</div>
)}
</>
)}
</FundForm>
<FundFooter />
</>
);
}
Track and respond to transaction status changes:
import {
Fund,
type FundProps,
} from '@coinbase/cdp-react';
function MonitoredFundFlow() {
const handleStatus: FundProps["onStatus"] = (status) => {
console.log('Transaction status:', status.statusName);
};
const handleSuccess: FundProps["onSuccess"] = (data) => {
console.log('Purchase successful:', data);
};
const handleError: FundProps["onError"] = (error) => {
console.error('Purchase error:', error);
};
return (
<Fund
country="US"
subdivision="CA"
cryptoCurrency="eth"
fiatCurrency="usd"
fetchBuyQuote={fetchBuyQuote}
fetchBuyOptions={fetchBuyOptions}
network="ethereum"
onStatus={handleStatus}
onSuccess={handleSuccess}
onError={handleError}
/>
);
}
country: ISO 3166-1 alpha-2 country code (e.g. "US") - typically obtained via IP geolocation and/or user selectioncryptoCurrency: The cryptocurrency to purchase (e.g., "eth", "btc")fiatCurrency: The fiat currency to use for payment (e.g., "usd", "eur")network: The blockchain network for the purchasefetchBuyQuote: Async function to fetch exchange rate quotesfetchBuyOptions: Async function to fetch available payment methodssubdivision: ISO 3166-2 subdivision code (e.g., "NY" for New York) - typically obtained via IP geolocation and/or user selectionpresetAmountInputs: Array of preset fiat amounts for quick selectioninputType: Initial input type ("fiat" or "crypto")openIn: Where to open the payment window ("tab" or "popup")redirectUrl: URL to redirect the user to after a successful transaction (note: the domain must be whitelisted in the CDP Portal Onramp section)submitLabel: Custom label for the submit buttontitle: Custom title for the fund interfacelocale: Locale for formatting (defaults to "en-US")cryptoDecimalPlaces: Number of decimal places for crypto amountsfiatDecimalPlaces: Number of decimal places for fiat amountsonStatus: Callback for transaction status changesonSuccess: Callback for successful transactionsonError: Callback for transaction errorschildren: React nodes or render function receiving FundState