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 theOrder 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 == signeris 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)
verifyingContract — this is the auth domain, not the order domain):
POST /auth/api-key, GET /auth/derive-api-key, etc.:
XO_ADDRESS — same code path as order signatures. The owner EOA produces the underlying ECDSA bytes.
L2 HMAC (signs every other private request)
AfterPOST /auth/api-key returns { apiKey, secret, passphrase }, sign each request with:
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 exposesplitPosition, 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.