# Tokenized Stocks
> How an AI agent buys and sells Coinbase's tokenized U.S. stocks on Base from its own wallet - the x402 routes, the calldata it signs, and a working script.
Source: https://docs.ozmium.org/agents/stocks/
Keywords: tokenized stocks base, coinbase b20 tokens, ai agent buy stocks onchain, x402 tokenized equity, nvdac aaplc googlc metac
Updated: 2026-09-02
---

Coinbase's tokenized U.S. stocks launched on Base on 24 August 2026 as **B20** tokens: ERC-20
compatible, eight decimals, one token tracking one share times an on-chain `multiplier()` that
absorbs splits and dividends. Secondary trading is permissionless, so any wallet can hold and trade
them. Ozmium quotes them, builds the calldata, and your agent signs it.

**Ozmium never signs and never takes custody.** You get `{ to, data, value }` back and broadcast it
from your own wallet, exactly as with every other builder on this API.

## The Three Routes

| Route | Price | What it returns |
| --- | --- | --- |
| `GET /v1/stocks` | $0.001 | The catalog: every ticker, its token address, Chainlink feed, live USD price, feed age, and whether it is minted and tradable. |
| `GET /v1/stocks/price` | $0.001 | A priced quote for one order without the calldata. |
| `POST /v1/tx/stocks` | $0.001 | The quote **plus** the steps to sign. |

Pay with USDC over [x402](/agents/x402/), or halve the price by
[paying in OZ](/agents/overview/). Surge pricing never applies to an OZ-credited call.

## Buying

```http
POST https://ozmium.org/v1/tx/stocks
Content-Type: application/json

{ "sym": "NVDAc", "side": "buy", "amount": "20", "taker": "0xYourAgentWallet", "slippageBps": 100 }
```

`amount` is human-scale: USDC to spend on a buy, shares to sell on a sell. The response carries
`steps` and an `advisory`:

```json
{
  "network": "eip155:8453",
  "steps": [
    { "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "data": "0x095ea7b3...", "value": "0" },
    { "to": "0x2626664c2603336E57B271c5C0b26F421741e481", "data": "0x04e45aaf...", "value": "0" }
  ],
  "advisory": {
    "amountOut": "8872500", "amountOutMin": "8783813", "pricePerShare": 225.41,
    "feedUsd": 225.2579, "feedUpdatedAt": 1788471000, "vsFeedPct": 0.069, "multiplier": 1,
    "pool": { "fee": 3000, "usdc": 23847, "shares": 54.86 }
  }
}
```

Step one is the USDC approve, prepended only when the live allowance is short. Step two is
`exactInputSingle` on Uniswap's `SwapRouter02`, with your `taker` as the recipient. Broadcast them in
order, or batch them in one user operation if your wallet is a smart account.

Selling is the same call with `"side": "sell"` and `amount` in shares. The approve is then on the
stock token.

## Why Uniswap and Not the Trade API

The CDP Trade API does not quote B20 tokens. Ozmium therefore measures the deepest USDC pool for the
token live, fills through `QuoterV2`, and returns the router calldata. The `advisory` shows the pool
it chose and its depth, so your agent can size the order against real liquidity rather than assume
it.

## Read the Advisory Before You Sign

- **`vsFeedPct`** is the pool fill against Chainlink's 24/5 total-return feed. A large positive
  number on a buy means you are paying a premium to thin liquidity. Refuse your own trade rather
  than signing it.
- **`feedUpdatedAt`** is the feed's own timestamp. Feeds freeze through corporate actions. A stale
  feed is a reason to wait, not a reason to widen slippage.
- **`pool.usdc`** and **`pool.shares`** are the reserves you are trading against. Only four names are
  minted today, and pool depth is measured in tens of thousands of dollars, not millions.
- **`multiplier`** converts token to share. Token price equals underlying times multiplier.

Unminted tickers return `409` with no pool rather than a fabricated quote.

## Jurisdiction

These tokens are issued under **Regulation S** and are not offered to U.S. persons. Minting and
redemption are gated by an on-chain authorization check; secondary transfers are not. Your agent is
responsible for its own eligibility. Ozmium routes a swap and takes no view on who you are.

## A Working Agent

This is the whole integration. It uses a bare private key, no Coinbase account, and no Ozmium
credential of any kind.

```javascript
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { base } from 'viem/chains'
import { x402Client, x402HTTPClient } from '@x402/core/client'
import { registerExactEvmScheme } from '@x402/evm/exact/client'

const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY)
const wallet = createWalletClient({ account, chain: base, transport: http() })
const pub = createPublicClient({ chain: base, transport: http() })

const x = new x402Client()
registerExactEvmScheme(x, { signer: account })
const http402 = new x402HTTPClient(x)

async function paid(url, init) {
  let res = await fetch(url, init)
  if (res.status !== 402) return res.json()
  const pr = http402.getPaymentRequiredResponse((n) => res.headers.get(n), await res.json())
  const payload = await http402.createPaymentPayload(pr)
  res = await fetch(url, { ...init, headers: { ...init.headers, ...http402.encodePaymentSignatureHeader(payload) } })
  return res.json()
}

const order = { sym: 'NVDAc', side: 'buy', amount: '20', taker: account.address, slippageBps: 100 }
const { steps, advisory } = await paid('https://ozmium.org/v1/tx/stocks', {
  method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(order),
})

if (Math.abs(advisory.vsFeedPct) > 1) throw new Error('premium too wide against the feed')

for (const step of steps) {
  const hash = await wallet.sendTransaction({ to: step.to, data: step.data, value: BigInt(step.value) })
  await pub.waitForTransactionReceipt({ hash })
  console.log(hash)
}
```

The calldata arrives with the [ERC-8021 builder code](/agents/overview/) already appended. Do not
re-encode it.

## The Tickers

Thirteen names are registered. `GET /v1/stocks` reports `minted` and `tradable` per name, which is
the field to branch on: an unminted ticker has no pool and cannot be filled.

| Ticker | Underlying | Ticker | Underlying |
| --- | --- | --- | --- |
| AAPLc | Apple | MSFTc | Microsoft |
| AMZNc | Amazon | MSTRc | Strategy |
| COINc | Coinbase | NVDAc | NVIDIA |
| CRCLc | Circle | SNDKc | SanDisk |
| GOOGLc | Alphabet | SPCXc | SpaceX |
| INTCc | Intel | TSLAc | Tesla |
| METAc | Meta | | |

Humans get the same rail in the app: open any of these on
[Risk](https://ozmium.org/risk) and pick **Stocks** beside Gains and Avantis.
