Quickstart - Python
Buy YES tokens on a PrediX market using web3.py.

Quickstart flow: Setup -> Approve -> Quote -> Trade -> Done. Router handles CLOB -> AMM routing automatically.
Prerequisites
Section titled “Prerequisites”pip install web3 requestsYou need:
- A wallet private key on Unichain mainnet - gas is sponsored via paymaster for eligible users
- test-USDC on Unichain (get from Faucet, 10,000 per address)
Complete working example
Section titled “Complete working example”"""PrediX - Python QuickstartBuy YES tokens on a prediction market via Router contract."""import jsonimport timefrom web3 import Web3
# --- Config ---RPC_URL = "https://mainnet.unichain.org"PRIVATE_KEY = "0x..." # your private keyCHAIN_ID = 130 # Unichain mainnet
# Contract addresses (beta)ROUTER = "0x39043ba36CF86590668200fb4Be094b9259B917E"USDC = "0xB3FCA863dD0F6b496cCDDf6497Da5Dad67857F56" # beta TestUSDCPERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3"
# --- Setup ---w3 = Web3(Web3.HTTPProvider(RPC_URL))account = w3.eth.account.from_key(PRIVATE_KEY)print(f"Wallet: {account.address}")
# --- ABI (minimal) ---ERC20_ABI = json.loads('[{"name":"approve","type":"function","inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]}]')
ROUTER_ABI = json.loads("""[ { "name": "buyYes", "type": "function", "stateMutability": "nonpayable", "inputs": [ {"name": "marketId", "type": "uint256"}, {"name": "usdcIn", "type": "uint256"}, {"name": "minYesOut", "type": "uint256"}, {"name": "recipient", "type": "address"}, {"name": "maxFills", "type": "uint256"}, {"name": "deadline", "type": "uint256"} ], "outputs": [ {"name": "yesOut", "type": "uint256"}, {"name": "clobFilled", "type": "uint256"}, {"name": "ammFilled", "type": "uint256"} ] }, { "name": "quoteBuyYes", "type": "function", "stateMutability": "nonpayable", "inputs": [ {"name": "marketId", "type": "uint256"}, {"name": "usdcIn", "type": "uint256"}, {"name": "maxFills", "type": "uint256"} ], "outputs": [ {"name": "expectedYesOut", "type": "uint256"}, {"name": "clobPortion", "type": "uint256"}, {"name": "ammPortion", "type": "uint256"} ] }]""")
usdc = w3.eth.contract(address=USDC, abi=ERC20_ABI)router = w3.eth.contract(address=ROUTER, abi=ROUTER_ABI)
def approve_usdc(): """One-time: approve USDC to Permit2.""" tx = usdc.functions.approve( PERMIT2, 10**12 # 1M USDC ).build_transaction({ "from": account.address, "nonce": w3.eth.get_transaction_count(account.address), "chainId": CHAIN_ID, }) signed = account.sign_transaction(tx) tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Approved USDC: {receipt.transactionHash.hex()}")
def quote_buy_yes(market_id: int, usdc_amount: int) -> int: """Get expected YES output (non-view - uses eth_call).""" result = router.functions.quoteBuyYes( market_id, usdc_amount, 10 ).call({"from": account.address}) expected_out, clob, amm = result print(f"Quote: {expected_out} YES (CLOB: {clob}, AMM: {amm})") return expected_out
def buy_yes(market_id: int, usdc_amount: int, min_out: int): """Execute buyYes trade.""" deadline = int(time.time()) + 300 # 5 min tx = router.functions.buyYes( market_id, usdc_amount, min_out, account.address, 10, # maxFills deadline, ).build_transaction({ "from": account.address, "nonce": w3.eth.get_transaction_count(account.address), "chainId": CHAIN_ID, }) signed = account.sign_transaction(tx) tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Trade complete: {receipt.transactionHash.hex()}") return receipt
if __name__ == "__main__": # Step 1: Approve (run once) # approve_usdc()
# Step 2: Quote market_id = 1 usdc_in = 100 * 10**6 # 100 USDC (6 decimals) expected = quote_buy_yes(market_id, usdc_in)
# Step 3: Execute with 0.5% slippage min_out = expected * 995 // 1000 buy_yes(market_id, usdc_in, min_out)export PRIVATE_KEY="0x..."python quickstart.pyKey notes
Section titled “Key notes”- Price precision:
1e6= 100%. Prices range$0.01-$0.99. - USDC: 6 decimals.
100 * 10**6 = 100 USDC. - Quote functions are NOT
view- they must be called viaeth_call(.call()in web3.py). They return(0, 0, 0)on invalid market state, never revert. - marketId is
uint256(not bytes32). - maxFills: how many CLOB orders to try matching.
10is a good default. - Routing: Router tries CLOB first, then AMM (Uniswap v4) for remainder. Unused input refunded automatically.
Exchange functions (CLOB direct)
Section titled “Exchange functions (CLOB direct)”For advanced users who want to interact with the CLOB directly:
# Place limit order# placeOrder(marketId, side, price, amount, builder)# side: 0=BUY_YES, 1=SELL_YES, 2=BUY_NO, 3=SELL_NO# price: 1e6 = 100%, e.g. 450000 = $0.45
# Cancel order# cancelOrder(orderId) - orderId is bytes32
# View orderbook# getOrderBook(marketId, depth) -> yesBids, yesAsks, noBids, noAsks# getBestPrices(marketId) -> bestBidYes, bestAskYes, bestBidNo, bestAskNoSee Router integration for detailed Exchange API.
Next steps
Section titled “Next steps”- Router integration - Permit2, batch trading, CLOB direct
- API reference - REST API for market data
- WebSocket - real-time streaming