Integrate Frax Ramp

There are two ways to drop the on- and off-ramp into your app. Use the React package for React apps, or the hosted iframe for everything else. Both talk to the same hosted API — no registration or secrets required.

React package

Install the package and import its stylesheet once (e.g. in your root layout):

pnpm add @iqai/ramp-react

Render the widget with a destination address to let users buy frxUSD with fiat:

import '@iqai/ramp-react/styles.css';
import { CRYPTO_ASSET, FIAT_CURRENCY, FraxRampWidget } from '@iqai/ramp-react';

export function Checkout({ wallet }) {
  return (
    <FraxRampWidget
      destinationAddress={wallet.address}
      amount={100}
      fiatCurrency={FIAT_CURRENCY.USD}
      cryptoAsset={CRYPTO_ASSET.FRXUSD}
    />
  );
}

To let users sell frxUSD for a fiat payout, optionally pass an onSellDeposit callback. When present, a "Send with wallet" button appears that hands you the deposit details so you can move the tokens and return the transaction hash. If you omit it, the widget falls back to the manual path — the user sends the tokens themselves and pastes the tx hash.

<FraxRampWidget
  fiatCurrency={FIAT_CURRENCY.USD}
  cryptoAsset={CRYPTO_ASSET.FRXUSD}
  onSellDeposit={async ({ amount, receiver, token }) => {
    const txHash = await wallet.sendErc20(token, receiver, amount);
    return { txHash };
  }}
/>

Key props

PropTypeDescription
destinationAddressstringWallet that receives crypto on a buy.
amountnumberPrefilled amount.
fiatCurrencyFiatCurrencyFiat side, e.g. FIAT_CURRENCY.USD.
cryptoAssetCryptoAssetCrypto side, e.g. CRYPTO_ASSET.FRXUSD.
defaultSide'BUY' | 'SELL'Which direction the widget opens on.
appearance'light' | 'dark' | 'system'Theme. Defaults to system.
inheritShadcnbooleanInherit your app's shadcn theme tokens.
onSellDepositfunctionCalled on a sell; send the tokens and return { txHash }.
onOrderStatusChangefunctionReceives the latest order status.
onOrderCreatedfunctionFires once when an order is created — { side, order }.
onErrorfunctionFires on a recoverable flow error — { side, message }.

Hosted iframe

For non-React apps, embed the hosted widget and configure it through query params. Pass parentOrigin so the iframe can post events back to your page:

<iframe
  id="frax-ramp"
  src="https://fraxramp.com/embed?cryptoAsset=FRXUSD&fiatCurrency=USD&parentOrigin=https%3A%2F%2Fapp.example.com"
  style="width: 420px; height: 640px; border: 0"
></iframe>

Query params

ParamDescription
cryptoAssetCrypto asset, e.g. FRXUSD.
fiatCurrencyFiat currency, e.g. USD.
amountPrefilled amount.
defaultSideBUY or SELL.
destinationAddressDestination wallet for a buy.
appearancelight, dark, or system.
parentOriginYour app's origin, required to receive events.

Selling through the iframe

To open the sell flow, set defaultSide=SELL. Because you can't pass a function across the iframe boundary, the React onSellDeposit callback is bridged over postMessage:

  1. When the user reaches the deposit step, the iframe emits a sell_deposit_requested event carrying a requestId and the deposit request (amount, receiver, token, chainId).
  2. Your page moves the tokens with its own wallet and posts a sell_deposit_result message back into the iframe with the matching requestId and the resulting txHash (or an error).
  3. The widget confirms the deposit and moves on.
const iframe = document.getElementById('frax-ramp');

window.addEventListener('message', async (event) => {
  if (event.origin !== 'https://fraxramp.com') return;
  if (event.data?.source !== 'frax-ramp') return;
  if (event.data.type !== 'sell_deposit_requested') return;

  const { requestId, request } = event.data;
  try {
    const txHash = await wallet.sendErc20(request.token, request.receiver, request.amount);
    iframe.contentWindow.postMessage(
      { source: 'frax-ramp-host', version: 1, type: 'sell_deposit_result', requestId, txHash },
      'https://fraxramp.com'
    );
  } catch (err) {
    iframe.contentWindow.postMessage(
      { source: 'frax-ramp-host', version: 1, type: 'sell_deposit_result', requestId, error: String(err) },
      'https://fraxramp.com'
    );
  }
});

If you don't handle the event, the user can still complete the sell manually — the widget always shows a deposit address and a field to paste the transaction hash.

Listening for events

The iframe posts messages to your page. Every event carries source: 'frax-ramp' and version: 1. Always verify the origin and the source field before trusting an event:

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://fraxramp.com') return;
  if (event.data?.source !== 'frax-ramp') return;

  switch (event.data.type) {
    case 'ready':
      // widget mounted
      break;
    case 'resize':
      // event.data.height — adjust the iframe height
      break;
    case 'order_created':
      // event.data.side, event.data.order (orderId, redirect / deposit details)
      break;
    case 'order_status_changed':
      console.log(event.data.status);
      break;
    case 'error':
      console.warn(event.data.side, event.data.message);
      break;
  }
});
Event typePayloadDescription
readyWidget has mounted and is ready.
resizeheightNew content height in pixels.
order_createdside, orderAn order was created (before funding). Buy carries providerRedirectUrl; sell carries the deposit details.
order_status_changedstatusLatest order status object.
errorside, messageA recoverable error surfaced in the flow.
sell_deposit_requestedrequestId, requestHost should send the tokens and post back a sell_deposit_result.

REST API

Both integrations sit on top of a plain REST API. For the full endpoint reference, see the API reference.