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

# Smart Accounts

> XO smart account identity, signature types, ERC-1271 validation, and order hashing.

XO smart accounts let a user trade from an account controlled by an owner wallet. For MM integrations, the smart account **is** the maker — fund it directly. Funding only the owner wallet does not make the smart account tradable.

## Roles

| Term          | Meaning                                                                                          |
| ------------- | ------------------------------------------------------------------------------------------------ |
| Owner wallet  | EOA that controls the smart account and produces the raw ECDSA signature.                        |
| Smart account | On-chain ERC-4337 / ERC-1271 account that holds funds, grants approvals, and is the order maker. |
| Maker         | Address on the signed CTF order (`order.maker`). For XO, this is the smart account.              |
| Signer        | Address recorded on the signed CTF order (`order.signer`). For XO, this **must equal `maker`**.  |

## Signature types

| `signatureType` | Use                             | Currently accepted?                                                |
| --------------- | ------------------------------- | ------------------------------------------------------------------ |
| `0`             | Direct EOA order (`ecrecover`). | **No** — the XO orderbook does not accept EOA-signed orders today. |
| `3`             | XO smart account (ERC-1271).    | **Yes** — the only supported order signature.                      |

Build your bot around the smart-account maker model from day one. The on-chain CTF Exchange enforces this for `signatureType = 3`:

* `order.maker == order.signer` (the smart account is both).
* `order.maker.code.length > 0` (it must be a contract).
* The signature passes `IERC1271(maker).isValidSignature(orderHash, signature)`.

## Order signing

The exchange validates orders by hashing the `Order` struct against the EIP-712 domain below, then handing the result to ERC-1271 on the smart account. Your client must produce a byte-identical hash before signing — otherwise the exchange will reject the order.

### Order domain

```text theme={null}
EIP712Domain(string name, string version, uint256 chainId, address verifyingContract)
  name              = "XO Market CLOB"
  version           = "1"
  chainId           = 3223
  verifyingContract = 0x4bC5E872256D12E6017dfe466E04c867DC761B77
```

### Order struct and typehash

The struct has 14 fields, but the EIP-712 typehash covers only 13 of them — `signature` is **not** part of the hash.

```solidity theme={null}
bytes32 constant ORDER_TYPEHASH = keccak256(
    "Order(uint256 salt,address maker,address signer,address beneficiary,"
    "uint256 tokenId,uint256 makerAmount,uint256 takerAmount,"
    "uint256 expiration,uint128 nonce,bytes16 identifier,bytes32 metadata,"
    "uint8 side,uint8 signatureType)"
);
```

### Hashing reference (Solidity)

This is the exact function the CTF Exchange uses to compute the order hash. Your client (`viem.signTypedData`, `ethers.signTypedData`, `eth_account.account.sign_typed_data`, etc.) must produce the same `bytes32` value.

```solidity theme={null}
function hashOrder(Order memory order) public view returns (bytes32) {
    return _hashTypedDataV4(
        keccak256(
            abi.encode(
                ORDER_TYPEHASH,
                order.salt,
                order.maker,
                order.signer,
                order.beneficiary,
                order.tokenId,
                order.makerAmount,
                order.takerAmount,
                order.expiration,
                order.nonce,
                order.identifier,
                order.metadata,
                order.side,
                order.signatureType
            )
        )
    );
}
```

`_hashTypedDataV4` is OpenZeppelin's standard EIP-712 helper — the final digest is `keccak256("\x19\x01" || domainSeparator || structHash)` where `domainSeparator` is built from the order domain above.

### Smart-account order fields

| Field                         | Value                                                                                                                                                                        |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `maker`                       | XO smart account address (the funded trading address). Funds principal + any fee; receives refunded unspent principal.                                                       |
| `signer`                      | **Same** XO smart account address. The exchange requires `maker == signer` for `signatureType = 3`.                                                                          |
| `beneficiary`                 | Who receives fill proceeds. `0x0000…0000` pays the maker (default). Never funds fees / never gets principal refunds.                                                         |
| `signatureType`               | `3` (ERC-1271 smart-contract signature).                                                                                                                                     |
| `signature`                   | **65-byte ECDSA signature produced by the owner EOA** of the smart account; the smart-account contract validates it via `isValidSignature(orderHash, signature)` (ERC-1271). |
| `tokenId`                     | Decimal U256 CTF outcome token id (YES or NO leg).                                                                                                                           |
| `makerAmount` / `takerAmount` | 6-decimal micro units; encode tick price into the ratio.                                                                                                                     |
| `identifier` / `metadata`     | Optional opaque tags (`bytes16` / `bytes32`). Use zero bytes when unused. Covered by the hash.                                                                               |
| `side`                        | `0` (BUY) or `1` (SELL).                                                                                                                                                     |
| `salt`, `nonce`, `expiration` | Standard meanings — `nonce` is `uint128` and is used for on-chain cancellation.                                                                                              |

Orders do **not** carry a fee rate. See [Fees](/guides/fees) for how taker and maker fees are charged at settlement.

The owner EOA produces a normal `secp256k1` signature over the typed order hash. The smart account's ERC-1271 implementation checks that the signature recovers to a registered owner — that is what allows `signatureType = 3` orders to be authorized off-chain without a UserOperation per order.

For EOA orders (`signatureType = 0`, reference only — not currently accepted by the orderbook):

* `maker == signer` is the owner EOA.
* funds and approvals belong to the EOA.

## Auth (separate from order signing)

The wire-level API auth is independent of the order signature. Two layers:

### L1 ClobAuth (mints API keys)

```text theme={null}
ClobAuth(address address, string timestamp, uint256 nonce, string message)
```

EIP-712 domain (no `verifyingContract` — this is the auth domain, not the order domain):

```text theme={null}
name    = "ClobAuthDomain"
version = "1"
chainId = 3223
```

Headers on `POST /auth/api-key`, `GET /auth/derive-api-key`, etc.:

```text theme={null}
XO_ADDRESS:   <smart account address>
XO_SIGNATURE: <EIP-712 signature of the ClobAuth struct>
XO_TIMESTAMP: <unix seconds>
XO_NONCE:     <integer nonce>
```

For smart accounts, the L1 signature is verified via ERC-1271 on `XO_ADDRESS` — same code path as order signatures. The owner EOA produces the underlying ECDSA bytes.

### L2 HMAC (signs every other private request)

After `POST /auth/api-key` returns `{ apiKey, secret, passphrase }`, sign each request with:

```text theme={null}
timestamp + method + path-with-query + body
```

URL-safe base64 HMAC-SHA256. Headers:

```text theme={null}
XO_API_KEY
XO_TIMESTAMP
XO_PASSPHRASE
XO_SIGNATURE
```

Keep `XO_TIMESTAMP` within ±30 s of server time. Use `GET /time` to align.

## On-chain calls outside the orderbook

The XO orderbook API does not expose `splitPosition`, `mergePositions`, or `redeemPositions`. To mint, recombine, or redeem outcome tokens, the smart account calls the Conditional Tokens contract directly, routed as an ERC-4337 UserOperation via the XO bundler. See [Redeem positions](/guides/redeem-positions) for the `redeemPositions` calldata, index sets, and bundler submission.

## Troubleshooting

| Symptom                                | Check                                                                                                           |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Order signature is rejected            | Domain `name`/`version`/`chainId` (`3223`)/`verifyingContract`, plus `maker == signer` and `signatureType = 3`. |
| `maker != signer` error                | `order.signer` must be the smart account address, not the owner EOA.                                            |
| ERC-1271 validation fails              | The 65-byte signature must come from an owner EOA registered on the smart account, not a random key.            |
| Smart-account auth fails               | `XO_ADDRESS` is the smart account (not the owner EOA) and chain id is `3223`.                                   |
| Order fails after signature validation | Smart-account balance and CTF Exchange approvals.                                                               |
| HMAC request is rejected               | Timestamp drift, path-with-query, API key, secret, passphrase.                                                  |
