Skip to main content
The orderbook API does not move collateral. After a market settles, your XO smart account still holds ERC-1155 outcome tokens on ConditionalTokens. To convert them into XO USDC, the smart account must call redeemPositions as an ERC-4337 UserOperation through the XO bundler. Mainnet bundler: https://mainnet-bundler.xo.market/

When a position is redeemable

GET /claimable?address=<smart-account> lists positions the orderbook considers settled. Treat that list as a discovery hint, not the on-chain gate. ConditionalTokens.redeemPositions reverts with result for condition not received yet until the oracle has reported payouts. That happens when the market is closed on-chain (after the resolution period), not merely when the winner is recorded. Confirm on-chain before sending a UserOperation:
If the denominator is still 0, wait and poll. A non-zero denominator means both resolved winners and voided refunds can be redeemed with the same call.
XO CLOB markets are binary. The manager reports [1, 0] or [0, 1] when a winner is recorded, and [1, 1] when the market is voided. You do not choose an amount — redeemPositions burns the smart account’s full balance in each requested index set and pays stake × numerator / denominator in XO USDC.

Call shape

Always pass both index sets. The losing side burns with a zero payout (a no-op on the transfer). Passing both also covers voided markets, where each side pays out at 50%. msg.sender must be the smart account that holds the tokens. That is why the call is wrapped in a UserOperation executed by the smart account — signing redeemPositions from the owner EOA as a normal transaction does not redeem the smart account’s positions.

Submit through the bundler

  1. Encode redeemPositions calldata against Conditional Tokens (0xCcf1b2C676E2f9A13e5674be0e0DdFF6d0B13909).
  2. Build an ERC-4337 v0.7 UserOperation whose inner call is that calldata (to = Conditional Tokens, value = 0).
  3. Attach the XO paymaster (address from onboarding) with empty paymasterData. The bundler estimates paymaster gas limits.
  4. Sign the bundler-computed userOpHash with the owner EOA of the smart account (raw ECDSA, same key that signs orders).
  5. eth_sendUserOperation to https://mainnet-bundler.xo.market/.
  6. Wait for the UserOperation receipt and check success. A reverted UserOperation is still included in a bundle transaction that succeeds — do not treat the outer transaction hash as proof that the redeem ran.
The XO bundler accepts zero EIP-1559 fees:
EntryPoint is ERC-4337 v0.7: 0x0000000071727De22E5E9d8BAf0edAc6f37da032.
The paymaster allowlists ConditionalTokens.redeemPositions, so redemption is gas-sponsored. The smart account does not need a native-token balance. Ask your XO contact for the mainnet paymaster address during onboarding.

TypeScript (viem)

account is the same XO smart account you use as order.maker. How that object encodes execute / executeBatch is wallet-specific (Kernel ERC-7579 execute(bytes32,bytes) on accounts created at beta.xo.market). The inner call above is what must reach Conditional Tokens.

Several markets in one UserOperation

De-dupe by (collateralToken, conditionId) and batch one redeemPositions per condition. Passing [1, 2] redeems every outcome the smart account holds on that condition, so two /claimable rows for the same voided market become a single call.

Payout math

Shares and collateral are 6-decimal. A /claimable balance of "50000000" is 50 shares. On success, Conditional Tokens emits PayoutRedemption(redeemer, collateralToken, parentCollectionId, conditionId, indexSets, payout) and transfers payout XO USDC to the smart account.

Troubleshooting

  • Market lifecycle — when /claimable starts listing a position.
  • Smart accounts — owner vs smart account, and why on-chain CT calls go through the bundler.
  • GET /claimable — settled positions the orderbook knows about.