> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Trade tokenized stocks with Ondo

Let users buy and sell tokenized stocks and ETFs from their Openfort embedded wallet. [Ondo Global Markets](https://ondo.finance/global-markets) issues tokens such as NVDAon (NVIDIA), TSLAon (Tesla) and SPYon (S\&P 500) that track the price of the underlying asset. On Solana they trade against USDC in on-chain pools, so the recipe gets a quote and a ready-to-sign transaction from [Jupiter](https://jup.ag), and the embedded wallet signs it. No backend is needed.

With Openfort and Ondo together, your app can:

* Create a Solana embedded wallet on sign-in, recovered with a passkey or password
* Quote USDC → stock and stock → USDC for five Ondo tokens
* Sign and send the swap from the embedded wallet, then refresh SOL, USDC and token balances

[View Sample Code](https://github.com/openfort-xyz/recipes-hub/tree/main/ondo) — GitHub Repository. Complete source code for the Ondo tokenized stocks recipe.

:::danger[Eligibility]
Ondo Global Markets tokens are **not available to U.S. persons**, to anyone placing orders from the United States, or to anyone in a prohibited jurisdiction, which includes Canada and sanctioned countries. In the UK, the EEA, Hong Kong, Singapore, Brazil, Malaysia and Switzerland they are limited to professional, qualified or accredited investors, and Swiss individuals cannot hold them at all. The tokens are not shares and carry no shareholder rights.

The sample shows a notice and asks the user to confirm eligibility before it shows the trade screen. That self-attestation is not KYC. Before launch, your app must check eligibility itself, with KYC and geo-blocking, and get its own legal review. See [Ondo's eligibility rules](https://docs.ondo.finance/ondo-stocks/eligibility).
:::

## Build it with your agent

Adding this to an app you already have? Set up your coding agent with the [Openfort docs MCP server and skill](https://www.openfort.io/docs/overview/building-with-ai), then give it this prompt:

```text
Add Ondo tokenized stock trading to this app's Openfort Solana embedded wallet. Follow the "Add this to your app" section of
https://github.com/openfort-xyz/recipes-hub/blob/main/ondo/AGENTS.md
```

The same file lists every Openfort primitive the recipe uses and the errors you're likely to hit, with their fixes.

## Run the sample

```bash
pnpx gitpick openfort-xyz/recipes-hub/tree/main/ondo openfort-ondo
cd openfort-ondo
pnpm install
cp .env.example .env
pnpm dev
```

The sample runs on **Solana mainnet**, because that's the only network where Ondo tokens exist. Send the wallet a little SOL for network fees and token-account rent, plus the USDC you want to trade.

## How it works

| Step | Call | Notes |
| --- | --- | --- |
| Wallet | `useSolanaEmbeddedWallet()` | `chainType: SVM` makes sign-in create or recover the Solana wallet |
| Quote | `GET https://lite-api.jup.ag/swap/v1/quote` | USDC → token to buy, token → USDC to sell; 3% slippage |
| Build | `POST https://lite-api.jup.ag/swap/v1/swap` | Returns an unsigned, base64 transaction with the user as fee payer |
| Sign | `provider.signTransaction({ messageBytes })` | The embedded wallet signs the message bytes |
| Send | `sendTransaction`, `getSignatureStatuses` | `@solana/kit` sends the signed transaction and waits for `confirmed` |
| Balances | `useSolanaWalletAssets()` | Reads SOL, SPL and Token-2022 balances |

### Configure the provider for Solana

The recipe is Solana-only, so it uses `OpenfortProvider` on its own, without wagmi. Set `chainType` to `SVM`. Otherwise sign-in picks the EVM wallet and `useSolanaEmbeddedWallet` never connects.

```tsx
// src/Providers.tsx
<OpenfortProvider
  publishableKey={import.meta.env.VITE_OPENFORT_PUBLISHABLE_KEY}
  walletConfig={{
    shieldPublishableKey: import.meta.env.VITE_OPENFORT_SHIELD_PUBLISHABLE_KEY,
    chainType: ChainTypeEnum.SVM,
    solana: { cluster: 'mainnet-beta', rpcUrls: { 'mainnet-beta': RPC_URL } },
  }}
  uiConfig={{
    authProviders: [AuthProvider.EMAIL_OTP, AuthProvider.GOOGLE],
    walletRecovery: {
      allowedMethods: [RecoveryMethod.PASSKEY, RecoveryMethod.PASSWORD],
      defaultMethod: RecoveryMethod.PASSKEY,
    },
  }}
>
```

Passkey and password recovery keep the sample backend-free. For production, switch to [automatic recovery](https://www.openfort.io/docs/configuration/recovery-methods).

### Quote and build the swap

Ondo tokens are Token-2022 mints with 9 decimals. `maxAccounts: 33` keeps multi-hop routes through Ondo's pools inside Solana's per-transaction account limit:

```ts
// src/ondo/jupiter.ts
const params = new URLSearchParams({
  inputMint,
  outputMint,
  amount: amount.toString(),
  slippageBps: '300',
  maxAccounts: '33',
})
return jupiter<Quote>(`/quote?${params}`)
```

`getSwapTransaction(quote, provider.publicKey)` posts that quote to `/swap` and returns the transaction as a base64 string.

### Sign with the embedded wallet

The embedded wallet signs the transaction's message bytes. The recipe adds the signature to the transaction and sends it:

```ts
// src/ondo/send.ts
const transaction = getTransactionDecoder().decode(getBase64Encoder().encode(base64Transaction))
const { signature } = await provider.signTransaction({
  messageBytes: new Uint8Array(transaction.messageBytes),
})
const signed = {
  ...transaction,
  signatures: { ...transaction.signatures, [signer]: toEd25519Signature(signature) },
}
const txSignature = await rpc
  .sendTransaction(getBase64EncodedWireTransaction(signed), { encoding: 'base64' })
  .send()
```

The signer can return a 65-byte signature with a trailing recovery byte, which Solana rejects. `toEd25519Signature` trims it to 64 bytes. Send the transaction as soon as you build it: Jupiter's blockhash expires after about a minute.

## Configuration

```bash
VITE_OPENFORT_PUBLISHABLE_KEY=       # pk_... (a live key if test mode rejects mainnet)
VITE_OPENFORT_SHIELD_PUBLISHABLE_KEY=
VITE_SOLANA_RPC_URL=                 # Optional; defaults to api.mainnet-beta.solana.com
```

## Things to know

* **Why Solana.** Ondo tokens also exist on Ethereum and BNB Chain, but minting and redeeming directly with Ondo requires Ondo KYC, which is open only to institutions for now. On Solana, anyone can trade the tokens against USDC through Jupiter, so an embedded wallet needs no partner API.
* **The user pays network fees in SOL.** `solana.sponsorFees` covers only sends made through Openfort's own send flow. It does not cover a transaction built outside Openfort, such as Jupiter's.
* **The first buy of each token creates its token account.** Rent is about 0.002 SOL.
* **Rate limits.** `lite-api.jup.ag` needs no API key, but it rate-limits each IP and answers `429` when you go over. In production, call Jupiter's keyed API from your backend.
* **Check every mint.** Fake tokens copy Ondo's symbols. Take mints from [jup.ag/tokens/ondo](https://jup.ag/tokens/ondo) and only list tokens Jupiter tags `ondo` and `verified`.

## Next steps

* [Solana wallets](https://www.openfort.io/docs/products/embedded-wallet/react/wallet/solana): create and manage Solana embedded wallets
* [Swap on LI.FI](https://www.openfort.io/docs/recipes/lifi): cross-chain swaps from an embedded wallet
* [Ondo Global Markets docs](https://docs.ondo.finance/ondo-global-markets/overview)
