Skip to main content
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

Signature types

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

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.

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.
_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

Orders do not carry a fee rate. See 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)

EIP-712 domain (no verifyingContract — this is the auth domain, not the order domain):
Headers on POST /auth/api-key, GET /auth/derive-api-key, etc.:
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:
URL-safe base64 HMAC-SHA256. Headers:
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 for the redeemPositions calldata, index sets, and bundler submission.

Troubleshooting