AAPLon, TSLAon and the rest. An Ondo offer arrives with type: "ondo::swap", and the buyer spends a stablecoin to receive the tokenized asset.
Recommended: render
CheckoutContainer. It detects ondo::swap and renders this entire flow for you. This page covers what is specific to Ondo, and the rungs below the component.ondo::swap sale type: buy (invest in tokenized stocks) and sell (liquidate holdings). Only buy ships today; sell will get its own page.
What Ondo needs from you
One config entry, and only one field of it is required:symbol is Ondo’s API symbol, not the on-chain symbol() and not necessarily offer.asset.code. Ondo’s symbol tracks the underlying ticker and changes on a rebrand, and the two already disagree on Sepolia, where a mock asset stands in. Return AssetSymbol(offer.asset.code) if your catalogue agrees; map the exceptions if it does not.
It must also be total - see the warning on the checkout page for why it runs for offers Ondo has nothing to do with.
The config also accepts an optional onOrderConfirmed(order), fired once a swap has mined. The confirmation dialog shows either way; add it only if you want to navigate, refresh a portfolio, or log.
Quotes are priced against Ondo production whatever chain you pass, because Ondo runs no sandbox. On a testnet the price is real and the money is not.
Placing an order takes two calls
This is the one structural thing to know about Ondo. An order isprepareSwap then executeSwap, not one call, and the split is where the buyer’s decisions are:
prepareSwapapproves the swap contract to spend the amount, then builds the transaction that spends it. The result is a committed quote: firm calldata with anexpiresAt, valid for about a minute.- The buyer reviews firm numbers - what they pay, the fee, what they receive.
executeSwapbroadcasts that calldata verbatim and waits for it to mine.
No CoinList fee is applied to a read quote, and no read discloses one. Ondo prices exactly the amount passed. A built transaction carries the fee explicitly, taken off the deposit rather than added on top, so the approval never has to cover more than
payInputAmount.Build your own UI with hooks (L2)
Build your own UI with hooks (L2)
useOndoBuyCheckoutViewModel wraps the whole flow - trading status, quotes, the wallet step, the approval, the review and the broadcast - and returns { state, onEvent }.state is a discriminated union - switch on it exhaustively and assign the default to const exhaustive: never = state so a new step is a compile error rather than a blank screen.The smaller hooks it is composed from are exported too, so you can assemble your own viewmodel instead:The three that fetch or poll take
enabled; the four viewmodels gate on their step input instead. All of them must be called unconditionally. See SDK structure for the conventions they share.Drive it yourself with the client (L1)
Drive it yourself with the client (L1)
coinlist.ondo is a plain namespace with no React. Three methods are backed by the API; two more need a wallet and so exist only on the browser client.On the server,
coinlist.ondo exposes the three reads only: a backend has no wallet to sign with.prepareSwap phases, in order. An allowance that already covers the order skips the approval phases:checking-allowance → (resetting-allowance → confirming-allowance-reset, only for stale non-zero allowances) → approving (wallet popup) → confirming-approval → building-transactionexecuteSwap phases: broadcasting-swap (wallet popup) → confirming-swapTwo error steps are worth calling out:insufficient-allowanceis separated from the generic build failure because it has a remedy. Reaching it means the approval that just mined is not the one the backend sees, usually an RPC node a block behind. That is a retry, not a re-approval.quote-expiredandspender-mismatchare refusals to broadcast, not failed broadcasts. Both would revert on-chain and cost the buyer gas, and both are fixed by building a fresh transaction.
executeSwap broadcasts the calldata verbatim. Never re-encode it: the contract verifies a signature over the exact arguments inside.Next steps
Building the Checkout flow
The recommended path: one container for every provider.
Errors and edge cases
Every error shape the flows return, and how to handle it.