> ## Documentation Index
> Fetch the complete documentation index at: https://docs.compasslabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tokenized Assets

> Tokenized Assets lets your users buy and hold tokenized US equities, RWA yield tokens, and managed vaults as real on-chain assets, settled into one account.

## Why This Matters

Offering tokenized stocks and real-world assets from scratch means integrating with issuers like Ondo, Midas, IXS, and Centrifuge, wiring up intent-based settlement, ERC-4626 vaults, and DEX routing, handling on-chain custody, and tracking positions across chains. We've done that - you get one API that covers all of it, with your tokenized-assets product live in days.

## What It Does

Let your users buy and hold real, on-chain tokens that represent equities and real-world assets. One product, one account, four asset families:

* **Tokenized equities (Ondo)** — fractional-share ERC-20s of stocks like Tesla, Apple, and Nvidia, traded via intent-based orders.
* **RWA yield tokens (Midas)** — NAV-accruing ERC-20s (`mTBILL`, `mBASIS`, `mBTC`) backed by real-world assets, traded via swaps.
* **Managed vaults (IXS)** — permissionless ERC-4626 RWA vaults on BNB Smart Chain; deposits mint instantly, redemptions settle asynchronously.
* **deRWA wrappers (Centrifuge)** — freely-transferable ERC-20 wrappers of Centrifuge V3 RWA fund shares (`deSPXA`, an S\&P 500 index fund, and `deJAAA`, a yield-bearing AAA-CLO fund), traded on the secondary market via DEX swaps. Regulation-S: not for US persons.

You call our API, we return an unsigned transaction or an EIP-712 payload. Users sign with their own wallet, tokens settle to their Tokenized Assets Account, you capture fees.

**Not perps.** This is distinct from [Global Markets](/v2/Products/Global-Markets), which offers synthetic perpetual futures with no token ownership. Tokenized Assets gives users **actual ERC-20 tokens** they hold and can transfer, settled on-chain.

**Important:** Users must first create a Tokenized Assets Account before trading. It's one isolated [product account](/v2/Products/Accounts) per owner at a deterministic address; the same account holds all three asset families across every supported chain. Tokens move in and out of that account, never the owner's wallet directly.

## Supported Markets

| Family             | Issuer     | Examples                                     | `asset_class`                         | Chains          | How to trade         |
| ------------------ | ---------- | -------------------------------------------- | ------------------------------------- | --------------- | -------------------- |
| Tokenized equities | Ondo       | `TSLAon`, `AAPLon`, `NVDAon`, `SPYon` (250+) | `EQUITY`                              | Ethereum        | Orders               |
| RWA yield tokens   | Midas      | `mTBILL`, `mBASIS`, `mBTC`                   | `T_BILLS`, `BASIS_TRADE`, `BTC_YIELD` | Ethereum, Base  | Swaps                |
| Managed vaults     | IXS        | `ixv1` (vault address)                       | `MANAGED_VAULT`                       | BNB Smart Chain | Swaps (async redeem) |
| deRWA wrappers     | Centrifuge | `deSPXA`, `deJAAA` (token address)           | `DERWA`                               | Base            | Swaps                |

Fetch the live catalog — current prices, 24h change, sector tags, TVL/APY for RWA assets, and the Reg-S `restricted_jurisdictions` flag — from `GET /v2/tokenized_assets/markets`, filterable by `provider`, `asset_class`, and `chain`.

## Getting Started

### Create a Tokenized Assets Account

`POST /v2/tokenized_assets/create_account` returns the deterministic account address for `owner` and an unsigned creation transaction. The address is **identical on every chain**, but the account must be deployed once per chain you trade on — pass `chain` (`ethereum` by default, `base` for Midas on Base, `bsc` for IXS). If the account already exists on that chain, `transaction` is `null` and you can skip straight to trading.

<CodeGroup>
  ```python Python theme={"system"}
  import httpx

  OWNER = "0xYourOwnerAddress"

  create = httpx.post(
      "https://api.compasslabs.ai/v2/tokenized_assets/create_account",
      json={"sender": OWNER, "owner": OWNER},   # add "chain": "base" | "bsc" for Midas / IXS
      headers={"x-api-key": "YOUR_API_KEY"},
  ).json()

  account = create["tokenized_assets_account_address"]
  if create["transaction"] is not None:
      # Sign and broadcast create["transaction"] with the owner's wallet, then cache the address.
      ...
  ```

  ```typescript TypeScript theme={"system"}
  const ownerAddress = "0xYourOwnerAddress";

  const create = await fetch(
    "https://api.compasslabs.ai/v2/tokenized_assets/create_account",
    {
      method: "POST",
      headers: { "Content-Type": "application/json", "x-api-key": "YOUR_API_KEY" },
      body: JSON.stringify({ sender: ownerAddress, owner: ownerAddress }),
    },
  ).then((r) => r.json());

  const account = create.tokenized_assets_account_address;
  if (create.transaction) {
    // Sign and broadcast create.transaction with the owner's wallet, then cache the address.
  }
  ```
</CodeGroup>

## Trade Equities (Ondo)

Tokenized equities trade through an intent-based settlement protocol: the user signs an off-chain order and professional market makers fill it on-chain. The **maker is the Tokenized Assets Account**, not the owner's wallet — the account holds the input tokens, authorizes the order, and receives the output tokens. The owner's wallet is its sole controller and signs everything off-chain.

Per order: build the order, sign a one-time token approval if one is returned, submit the signed order, then poll for the fill.

### 1. Build an order

`POST /v2/tokenized_assets/order` returns three things in one round-trip:

* `quote` — a preview of the input/output amounts and fees.
* `approval_safe_tx_eip712` — an EIP-712 payload, present **only** when the account's allowance to the settlement contract is below `amount`. `null` once the token is approved.
* `order` — the order metadata (`order_hash`, `order_message`, `extension`, `quote_id`) plus `safe_message_eip712`, the payload the owner signs to authorize the order.

<CodeGroup>
  ```python Python theme={"system"}
  build = httpx.post(
      "https://api.compasslabs.ai/v2/tokenized_assets/order",
      json={
          "from_token": "USDC",       # symbol or 0x-prefixed Ethereum address
          "to_token": "TSLAon",
          "amount": "100",            # 100 USDC; decimals applied server-side
          "owner": OWNER,
          "slippage_bps": 50,         # 0.5% (range 1–1000, max 10%)
      },
      headers={"x-api-key": "YOUR_API_KEY"},
  ).json()

  quote = build["quote"]
  approval = build["approval_safe_tx_eip712"]   # None once the token is approved
  order = build["order"]
  ```

  ```typescript TypeScript theme={"system"}
  const build = await fetch(
    "https://api.compasslabs.ai/v2/tokenized_assets/order",
    {
      method: "POST",
      headers: { "Content-Type": "application/json", "x-api-key": "YOUR_API_KEY" },
      body: JSON.stringify({
        from_token: "USDC",
        to_token: "TSLAon",
        amount: "100",
        owner: ownerAddress,
        slippage_bps: 50,
      }),
    },
  ).then((r) => r.json());

  const { quote, approval_safe_tx_eip712: approval, order } = build;
  ```
</CodeGroup>

<Info>If the account isn't deployed on this chain yet, the call returns `400 Tokenized Assets Account not deployed` — create it first. An unsupported `from_token`/`to_token` returns `404 Market not found`.</Info>

### 2. Approve the input token (first order per token only)

When `build.approval_safe_tx_eip712` is non-null, the owner signs it with `signTypedData`, then a relayer (or the owner) broadcasts it. This authorizes the settlement contract to pull the input token.

<CodeGroup>
  ```typescript Sign (viem) theme={"system"}
  import { createWalletClient, custom } from "viem";

  const wallet = createWalletClient({ transport: custom(window.ethereum) });

  let approvalSignature: `0x${string}` | undefined;
  if (approval) {
    approvalSignature = await wallet.signTypedData({
      account: ownerAddress,
      domain: approval.domain,
      types: approval.types,
      primaryType: approval.primaryType,   // "SafeTx"
      message: approval.message,
    });
  }
  ```

  ```python Relay (Python) theme={"system"}
  if approval is not None:
      prep = httpx.post(
          "https://api.compasslabs.ai/v2/gas_sponsorship/prepare",
          json={
              "chain": "ethereum",
              "product": "tokenized_assets",
              "owner": OWNER,
              "sender": SPONSOR,          # pays gas; can be the owner
              "eip_712": approval,
              "signature": approval_signature,
          },
          headers={"x-api-key": "YOUR_API_KEY"},
      ).json()
      # prep["transaction"] is an unsigned execTransaction — sign with sender's key and broadcast.
  ```
</CodeGroup>

<Info>Approval is **one-time per token**: the API requests an unlimited approval, so every future order against the same `from_token` skips this step.</Info>

<Warning>Wait for the approval transaction to confirm before submitting the order. If you submit too early, no market maker can pull the input token and the order won't fill.</Warning>

### 3. Submit the signed order

The owner signs `order.safe_message_eip712` and submits it to `POST /v2/tokenized_assets/order/submit`. Unlike the approval, this signature is **never broadcast on-chain** — it's relayed off-chain to the market-maker network, which validates it against the account at fill time.

<CodeGroup>
  ```typescript Sign (viem) theme={"system"}
  const orderSignature = await wallet.signTypedData({
    account: ownerAddress,
    domain: order.safe_message_eip712.domain,
    types: order.safe_message_eip712.types,
    primaryType: order.safe_message_eip712.primaryType,   // "SafeMessage"
    message: order.safe_message_eip712.message,           // { message: "0x<orderHash>" }
  });
  ```

  ```python Submit (Python) theme={"system"}
  submit = httpx.post(
      "https://api.compasslabs.ai/v2/tokenized_assets/order/submit",
      json={
          "signed_order": order["order_message"],   # maker is the account — pass back unchanged
          "signature": order_signature,
          "extension": order["extension"],           # opaque — pass back unchanged
          "quote_id": order["quote_id"],             # opaque — pass back unchanged
          "order_hash": order["order_hash"],         # recommended
      },
      headers={"x-api-key": "YOUR_API_KEY"},
  ).json()

  order_hash = submit["order_hash"]
  ```
</CodeGroup>

<Info>`extension` and `quote_id` are opaque values from the upstream quote — pass them back unchanged, don't inspect them. Including `order_hash` guarantees you get a usable handle for status and cancel lookups.</Info>

### 4. Poll for fill status

`GET /v2/tokenized_assets/order/{order_hash}` returns the lifecycle state:

* `pending` — waiting for a market maker (covers partial fills; `filled_amount` populates as fills arrive).
* `filled` — fully settled. `fill_tx_hash` and `filled_amount` are set, and the output token lands in the account.
* `expired` — the auction window closed without a fill.
* `cancelled` — cancelled on-chain before it filled.

```python Python theme={"system"}
status = httpx.get(
    f"https://api.compasslabs.ai/v2/tokenized_assets/order/{order_hash}",
    headers={"x-api-key": "YOUR_API_KEY"},
).json()

print(status["status"], status.get("fill_tx_hash"))
```

### 5. Cancel an unfilled order

`POST /v2/tokenized_assets/order/{order_hash}/cancel` returns `cancel_safe_tx_eip712`, which cancels the order on-chain when signed and broadcast — the same sign-then-relay pattern as the token approval. It works on `pending` and `expired` orders; a `filled` or already-`cancelled` order returns `409`, and only the maker account can cancel (`403` otherwise).

```python Python theme={"system"}
cancel = httpx.post(
    f"https://api.compasslabs.ai/v2/tokenized_assets/order/{order_hash}/cancel",
    json={"owner": OWNER},
    headers={"x-api-key": "YOUR_API_KEY"},
).json()
# Owner signs cancel["cancel_safe_tx_eip712"], then relay it via
# /v2/gas_sponsorship/prepare exactly like the approval in step 2.
```

## Trade RWA Yield (Midas)

Midas RWA tokens are ERC-20s whose NAV accrues yield from real-world assets. They trade on the **secondary market** via an on-chain DEX aggregator — there's no order lifecycle, just a swap. They're held in the same Tokenized Assets Account.

| Symbol   | Underlying           | `asset_class` | Chains         |
| -------- | -------------------- | ------------- | -------------- |
| `mTBILL` | US Treasury Bills    | `T_BILLS`     | Ethereum, Base |
| `mBASIS` | Crypto basis trading | `BASIS_TRADE` | Ethereum, Base |
| `mBTC`   | BTC yield            | `BTC_YIELD`   | Ethereum       |

### Buy and sell (swaps)

Fund the account with a plain ERC-20 transfer of the input token, then swap. `transact/buy` and `transact/sell` each return one unsigned transaction (or an EIP-712 payload with `gas_sponsorship: true`) plus an `estimated_amount_out`.

```python Python theme={"system"}
# Buy: swap USDC -> mTBILL inside the product account.
buy = httpx.post(
    "https://api.compasslabs.ai/v2/tokenized_assets/transact/buy",
    json={
        "token_in": "USDC",
        "token_out": "mTBILL",     # the RWA symbol
        "amount_in": "1000",
        "slippage": 0.005,
        "owner": OWNER,
        "chain": "ethereum",       # or "base"
    },
    headers={"x-api-key": "YOUR_API_KEY"},
).json()
# buy["transaction"]          — unsigned swap tx to sign and broadcast
# buy["estimated_amount_out"] — expected mTBILL out
#
# Sell: same call to /transact/sell with the RWA symbol as token_in.
```

<Warning>The order flow and the swap flow are mutually exclusive and enforced both ways: an equity symbol on `/transact/buy` or `/transact/sell` returns `422` pointing to the order flow, and an RWA symbol (`mTBILL`/`mBASIS`/`mBTC`) on `/quote` or `/order` returns `422 Wrong trade flow` pointing to the swap endpoints.</Warning>

## Managed Vaults (IXS)

IXS managed vaults are permissionless ERC-4626 RWA vaults on **BNB Smart Chain** whose NAV accrues yield from real-world assets, held through the same Tokenized Assets Account. One vault is surfaced today:

| Handle | Asset | `asset_class`   | Chain           |
| ------ | ----- | --------------- | --------------- |
| `ixv1` | USDC  | `MANAGED_VAULT` | BNB Smart Chain |

<Info>USDC on BNB Smart Chain is an **18-decimal** token (not 6, as on other chains). Pass human-readable amounts and the API scales them correctly.</Info>

### Deposit and redeem

Vaults use the same `transact/buy` / `transact/sell` endpoints as Midas, but you pass the **vault address** as the tokenized side (IXS shares aren't a registered symbol). The two sides settle differently.

```python Python theme={"system"}
# Deposit = instant ERC-4626 mint.
deposit = httpx.post(
    "https://api.compasslabs.ai/v2/tokenized_assets/transact/buy",
    json={
        "token_in": "USDC",
        "token_out": VAULT_ADDRESS,   # the IXS vault (ixv1)
        "amount_in": "1000",          # 18-decimal USDC; human-readable amount
        "slippage": 0.005,
        "owner": OWNER,
        "chain": "bsc",
    },
    headers={"x-api-key": "YOUR_API_KEY"},
).json()
# deposit["settlement"] == "instant" — shares mint to the account in the signed tx.
#
# Redeem = async request: /transact/sell with the vault address as token_in returns a
# requestRedeem tx (settlement: "async"). The vault operator settles it off-chain later.
```

### Redemption status

`GET /v2/tokenized_assets/redemptions` reconstructs the owner's redemption requests directly from the vault (Compass stores no async state). Poll it after a sell until the request is `finalized`.

```python Python theme={"system"}
redemptions = httpx.get(
    "https://api.compasslabs.ai/v2/tokenized_assets/redemptions",
    params={"owner": OWNER, "chain": "bsc", "vault": "ixv1"},
    headers={"x-api-key": "YOUR_API_KEY"},
).json()
# Each entry: status (pending | finalized | rejected), shares, and while pending,
# expected_net_assets — the amount it would settle for at the current NAV (a preview, not a guarantee).
```

## Trade deRWA (Centrifuge)

Centrifuge V3 issues **deRWA** tokens — freely-transferable ERC-20 wrappers of otherwise-restricted RWA fund shares. Unlike the KYC-gated primary vaults, deRWA trade permissionlessly on DEXs, so they use the same `transact/buy` / `transact/sell` swap flow as Midas, held in the same Tokenized Assets Account. Two deRWA are surfaced today, both on Base:

| Handle   | Address                                      | Underlying                          | `asset_class` |
| -------- | -------------------------------------------- | ----------------------------------- | ------------- |
| `deSPXA` | `0x9c5C365e764829876243d0b289733B9D2b729685` | Anemoy S\&P 500 Index Fund          | `DERWA`       |
| `deJAAA` | `0xAAA0008C8CF3A7Dca931adaF04336A5D808C82Cc` | Janus Henderson Anemoy AAA CLO Fund | `DERWA`       |

`deJAAA` is **yield-bearing** — its NAV accrues CLO interest, so it surfaces `apy_7d`/`apy_30d`. `deSPXA` tracks an equity index (price movement, not yield), so its APY fields are null and it's presented by price + % change instead.

<Warning>deRWA are **Regulation-S** instruments: they must not be offered to US persons. Each deRWA market carries a `restricted_jurisdictions` field (e.g. `["US"]`) so you can geo-gate. The API surfaces the restriction but does **not** enforce it — geo-gating and any eligibility attestation are your responsibility.</Warning>

### Buy and sell (swaps)

Pass the deRWA **token address** as the tokenized side (deRWA aren't registered symbols). A buy swaps USDC → deRWA; a sell swaps deRWA → USDC through the on-chain DEX aggregator. Both settle instantly in one transaction. `current_price_usd` on the market is the token's **NAV** (not a DEX mid), while `estimated_amount_out` is the honest executable amount from the live swap quote.

```python Python theme={"system"}
# Buy: swap USDC -> deSPXA inside the product account (Base).
buy = httpx.post(
    "https://api.compasslabs.ai/v2/tokenized_assets/transact/buy",
    json={
        "token_in": "USDC",
        "token_out": DESPXA_ADDRESS,   # the deRWA token (deSPXA)
        "amount_in": "1000",
        "slippage": 0.01,
        "owner": OWNER,
        "chain": "base",
    },
    headers={"x-api-key": "YOUR_API_KEY"},
).json()
# buy["settlement"] == "instant"; buy["estimated_amount_out"] — expected deSPXA out.
#
# Sell: same call to /transact/sell with the deRWA address as token_in (payout in USDC).
```

<Info>Full price history — `apy_7d`/`apy_30d`, 24h change, a 24h sparkline, and OHLC candles — is served on the markets endpoints from indexed NAV history, exactly like Midas.</Info>

## Read Markets and Positions

### Markets and candles

`GET /v2/tokenized_assets/markets` lists every market; `GET /v2/tokenized_assets/markets/{symbol}` returns a single market with extended detail (52-week range, volume, market cap, holder count, tradable sessions) and an optional OHLC candle series.

Candles need a matching `interval` + `range` pair — pass both, or omit both to get the detail without candles:

| `interval`              | Allowed `range`                    |
| ----------------------- | ---------------------------------- |
| `1min`, `5min`, `15min` | `1day`                             |
| `1hour`, `4hour`        | `1month`                           |
| `12hour`                | `3month`                           |
| `1day`                  | `3month`, `6month`, `1year`, `all` |

```python Python theme={"system"}
detail = httpx.get(
    "https://api.compasslabs.ai/v2/tokenized_assets/markets/TSLAon",
    params={"interval": "1hour", "range": "1month"},
    headers={"x-api-key": "YOUR_API_KEY"},
).json()
```

### Positions

`GET /v2/tokenized_assets/positions` returns the balances of every tokenized asset in the owner's account — Ondo equities, Midas RWA tokens, IXS vault shares, and Centrifuge deRWA — each enriched with a current USD price (RWA at the latest indexed NAV, IXS vaults at live on-chain NAV, deRWA at the Centrifuge NAV) plus a `total_usd` aggregate.

Pass the **owner's wallet address**; the API derives the account and reads balances from there (fills, swaps, and deposits all land in the account, not the wallet). The read is per-chain and `chain` is optional, defaulting to `ethereum` — pass `base` for Midas or Centrifuge holdings, or `bsc` for IXS holdings.

```python Python theme={"system"}
positions = httpx.get(
    "https://api.compasslabs.ai/v2/tokenized_assets/positions",
    params={"owner": OWNER},   # chain optional: defaults to "ethereum"; "base" / "bsc" for Midas / IXS
    headers={"x-api-key": "YOUR_API_KEY"},
).json()

print(positions["total_usd"])
```

<AccordionGroup>
  <Accordion title="Error reference" icon="triangle-exclamation">
    All `4xx`/`5xx` responses use the standard Compass `{ "error": "<error>", "message": "<text>" }` envelope, except input-validation `422`s, which come from FastAPI as `{ "detail": [...] }`.

    | Error                                   | Status | When                                                                                    |
    | --------------------------------------- | ------ | --------------------------------------------------------------------------------------- |
    | `Tokenized Assets Account not deployed` | 400    | No account on this chain yet — call `/create_account` first.                            |
    | `Market not found.`                     | 404    | Unknown symbol, or `from_token`/`to_token` can't be resolved.                           |
    | `Order not found.`                      | 404    | Unknown order hash on status or cancel.                                                 |
    | `Not the order maker`                   | 403    | The account predicted from `owner` isn't the order's maker.                             |
    | `Insufficient liquidity`                | 409    | No market maker could fill at this size — try smaller, or retry shortly.                |
    | `Order already filled`                  | 409    | Cancel attempted on a filled order.                                                     |
    | `Order already cancelled`               | 409    | Cancel attempted on an already-cancelled order.                                         |
    | `Quote expired`                         | 410    | Quote stale at submit time — re-quote and retry.                                        |
    | `Slippage exceeded`                     | 422    | The auction may fill below your `slippage_bps` — raise it or pick a more liquid market. |
    | `Wrong trade flow`                      | 422    | An RWA symbol was sent to the equity flow — use `/transact/buy` or `/transact/sell`.    |
    | `Market data unavailable`               | 502    | Market/price/status data is temporarily unavailable.                                    |
    | `Swap service unavailable`              | 502    | The swap service (quote, order, submit, cancel) is temporarily unavailable.             |
  </Accordion>
</AccordionGroup>

## Use Cases

**Brokerage app for retail.** Let users buy fractional Tesla or Apple shares with USDC, hold them as on-chain tokens, and sell any time without a centralized broker.

*Example: a user buys \$500 of `TSLAon`, holds it as an ERC-20, and sells back to USDC a month later. You embed a fee on each trade.*

**On-chain portfolio diversification.** Crypto-native users get equity and T-bill exposure without leaving their wallet — the same UX as a token swap, settled just-in-time on-chain.

*Example: a user rotates \$10,000 of idle USDC into `mTBILL` to earn short-term Treasury yield while staying fully on-chain.*

**Treasury allocation.** DAOs and on-chain orgs rebalance treasuries into tokenized equities or RWA yield while keeping signing authority with the owner wallet — no third-party custody.

*Example: a DAO parks \$250,000 of its stablecoin treasury in `mBASIS` and tracks NAV live from the positions endpoint.*

## Next Steps

<CardGroup cols={3}>
  <Card title="Product Accounts" icon="wallet" href="/v2/Products/Accounts">
    Background on the per-product account model used here.
  </Card>

  <Card title="Gas Sponsorship" icon="gas-pump" href="/v2/Products/gas-sponsorship">
    How `/v2/gas_sponsorship/prepare` broadcasts the signed approval and cancel transactions.
  </Card>

  <Card title="Global Markets" icon="chart-line" href="/v2/Products/Global-Markets">
    Synthetic stock perps via Hyperliquid — a different product, with leverage.
  </Card>
</CardGroup>
