When to reach for this
- A fintech offering “USD → USDC on Solana” in-app
- A merchant accepting USDC and settling to fiat on their bank cut-off
- A treasury moving between fiat reserves and on-chain working capital
Setup
1
Verify the withdrawal account(s)
For an on-ramp, verify the stablecoin wallet on the chain you want to
deliver to. For an off-ramp, verify the bank account that should
receive the fiat. Both happen in the OpenFX
dashboard. See Verified
accounts for the walkthrough.
2
Confirm the chain
The
network field on a stablecoin withdrawal account binds the chain. USDC
on Solana and USDC on Ethereum are separate ledgers — see Supported
networks for the full stablecoin × chain matrix.
Pick the chain your end-user actually expects to receive on.3
Subscribe to webhooks
Both flows benefit from the
deposits event (so you react the moment funds
arrive) and the withdrawals event (so your customer’s “your USDC is on
Ethereum” message fires the second the on-chain tx confirms). Each webhook’s
data is the resource object, so the terminal state is in data.status.
See Webhooks setup.On-ramp: fiat → stablecoin
1. Detect the fiat deposit
The customer’s wire arrives. OpenFX detects it (Deposit lifecycle) and either fires a webhook or you find it onGET /v3/fx/deposits. Match it to your internal user via the senderReference your customer was told to put in the wire memo.
2. Quote USD → USDC
cURL
3. Execute the trade
cURL
4. Withdraw to the customer’s chain-specific wallet
ThewithdrawalAccountId selects both currency (USDC) and chain (Solana). You don’t pass a network field — it’s bound to the account.
COMPLETED once the chain reaches the configured confirmation count. The response carries network and transactionHash so you can surface the chain explorer link to your user.
See Supported networks for per-chain request examples (Ethereum, Solana, Tron, BSC, etc.).
Off-ramp: stablecoin → fiat
1. Receive the stablecoin deposit
The customer sends USDC to your OpenFX deposit address. The deposit record’snetwork tells you which chain it landed on; transactionHash is the on-chain reference for support tickets.
status: "COMPLETED" — that’s when the funds are spendable. A stablecoin deposit appears as PENDING the moment OpenFX observes the on-chain transfer, then COMPLETED once it clears (or ERROR if it doesn’t). See Deposit lifecycle.
2. Quote USDC → USD
Same shape, opposite direction:3. Execute the trade
SamePOST /v3/fx/trades as the on-ramp. After execution, USDC has decreased and USD has increased.
4. Withdraw to the customer’s bank account
Choosing the right chain on on-ramps
When your UX lets a user pick the chain to receive on, the choice matters:
The full chain matrix per stablecoin (USDC, USDT, EURC) is at Supported networks.
Common mistakes
- Treating “USDC” as one chain. A withdrawal to a Solana wallet can’t be sent on an Ethereum
withdrawalAccountId— they’re separate addresses, separate ledgers. Confirm the verified withdrawal account’snetworkmatches the user’s expectation before withdrawing. Sending to the wrong-chain address is not recoverable. - Quoting before the deposit clears. A
PENDINGdeposit isn’t usable balance yet. Wait forCOMPLETED(or for the webhook) before issuing the quote. - Polling chain explorers instead of
GET /v3/fx/withdrawals/{id}. OpenFX’s own state machine is the source of truth; the explorer’s view can lag (or, for some chains, race ahead before OpenFX considers it confirmed). - Quoting first, then realizing the wallet isn’t verified. Set up the verified withdrawal account before the user’s first on-ramp; the quote → trade → withdrawal chain assumes the account is already in place.
What’s next
Supported networks
Stablecoin × chain matrix + per-chain request bodies.
Settlement times
Fiat-rail cut-offs by currency.
Deposit lifecycle
How deposits get from observed → credited.
Cross-border payments
The fiat-only sibling of this flow.