Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
Predict SDK for Python
https://github.com/predictdotfun/sdk-python
Admin
A Python SDK designed to help developers interface with the Predict.fun protocol, enabling
...
Tokens:
12,059
Snippets:
72
Trust Score:
7.5
Update:
1 month ago
Context
Skills
Chat
Benchmark
82.5
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Predict SDK for Python The Predict SDK is a Python library that enables developers to interface with the [Predict.fun](https://predict.fun/) prediction market protocol on BNB Chain. It provides tools for building, signing, and managing trading orders for conditional token framework (CTF) markets, including standard markets and NegRisk (winner-takes-all) markets with optional yield-bearing features. The SDK's core functionality revolves around the `OrderBuilder` class, which handles order creation, EIP-712 signing, contract approvals, position management (redeem/merge/split), and balance queries. It supports both externally owned accounts (EOA) and Predict Accounts (Kernel smart wallets), with synchronous and asynchronous APIs for all contract interactions. The library uses web3.py for blockchain communication and provides type-safe dataclasses for all order and configuration types. ## OrderBuilder.make Factory method to create an `OrderBuilder` instance. Creates instances with or without a signer - read-only mode (no signer) allows order calculation and building, while signer mode enables signing and contract interactions. ```python from predict_sdk import OrderBuilder, ChainId, OrderBuilderOptions # Read-only mode (no signer) - can calculate amounts and build orders builder = OrderBuilder.make(ChainId.BNB_MAINNET) # With private key signer - can sign orders and interact with contracts private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # With Predict Account (smart wallet) - for Privy exported wallets builder = OrderBuilder.make( ChainId.BNB_MAINNET, private_key, OrderBuilderOptions( predict_account="0x742d35Cc6634C0532925a3b844Bc9e7595f1E123", precision=18, # Default: 18 decimals for wei log_level="INFO", # Options: ERROR, WARN, INFO, DEBUG ), ) ``` ## get_limit_order_amounts Calculate the maker and taker amounts for a LIMIT order based on price per share and quantity. Returns `OrderAmounts` containing the calculated values for order building. ```python from predict_sdk import OrderBuilder, ChainId, Side, LimitHelperInput builder = OrderBuilder.make(ChainId.BNB_MAINNET) # Calculate amounts for a BUY limit order at 0.5 USDT per share for 10 shares amounts = builder.get_limit_order_amounts( LimitHelperInput( side=Side.BUY, price_per_share_wei=500000000000000000, # 0.5 USDT in wei quantity_wei=10000000000000000000, # 10 shares in wei ) ) print(f"Price Per Share: {amounts.price_per_share}") # 500000000000000000 print(f"Maker Amount (USDT to spend): {amounts.maker_amount}") # 5000000000000000000 print(f"Taker Amount (shares to receive): {amounts.taker_amount}") # 10000000000000000000 # For SELL orders, maker_amount is shares and taker_amount is USDT sell_amounts = builder.get_limit_order_amounts( LimitHelperInput( side=Side.SELL, price_per_share_wei=600000000000000000, # 0.6 USDT per share quantity_wei=5000000000000000000, # 5 shares ) ) print(f"Maker Amount (shares to sell): {sell_amounts.maker_amount}") # 5000000000000000000 print(f"Taker Amount (USDT to receive): {sell_amounts.taker_amount}") # 3000000000000000000 ``` ## get_market_order_amounts Calculate market order amounts using the orderbook. Supports quantity-based orders (for BUY/SELL) and value-based orders (BUY only). Includes optional slippage tolerance. ```python from predict_sdk import ( OrderBuilder, ChainId, Side, MarketHelperInput, MarketHelperValueInput, Book ) builder = OrderBuilder.make(ChainId.BNB_MAINNET) # Sample orderbook from GET /orderbook/{marketId} orderbook = Book( market_id=123, update_timestamp_ms=1700000000000, asks=[(0.45, 100.0), (0.46, 200.0), (0.47, 150.0)], # (price, quantity) bids=[(0.44, 100.0), (0.43, 200.0), (0.42, 150.0)], ) # Market BUY by quantity (10 shares) amounts = builder.get_market_order_amounts( MarketHelperInput( side=Side.BUY, quantity_wei=10000000000000000000, # 10 shares slippage_bps=100, # 1% slippage tolerance is_min_amount_out=True, # Deflate taker (min shares out) ), orderbook, ) print(f"Maker Amount (cost): {amounts.maker_amount}") print(f"Taker Amount (min shares): {amounts.taker_amount}") print(f"Actual Shares: {amounts.amount}") print(f"Slippage Applied: {amounts.slippage_bps} bps") # Market BUY by value (spend 5 USDT) value_amounts = builder.get_market_order_amounts( MarketHelperValueInput( side=Side.BUY, value_wei=5000000000000000000, # 5 USDT slippage_bps=50, # 0.5% slippage is_min_amount_out=True, ), orderbook, ) # Market SELL by quantity sell_amounts = builder.get_market_order_amounts( MarketHelperInput( side=Side.SELL, quantity_wei=10000000000000000000, slippage_bps=100, # Decreases taker_amount (accept less USDT) ), orderbook, ) ``` ## build_order Build an order object for MARKET or LIMIT strategies. The order can then be signed and submitted to the exchange. ```python from predict_sdk import ( OrderBuilder, ChainId, Side, SignatureType, BuildOrderInput, LimitHelperInput ) from datetime import datetime, timezone, timedelta private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Calculate amounts first amounts = builder.get_limit_order_amounts( LimitHelperInput( side=Side.BUY, price_per_share_wei=500000000000000000, quantity_wei=10000000000000000000, ) ) # Build a LIMIT order order = builder.build_order( "LIMIT", BuildOrderInput( side=Side.BUY, token_id="48792138471239847123984712398471239847", # From market data maker_amount=str(amounts.maker_amount), taker_amount=str(amounts.taker_amount), fee_rate_bps=100, # Get from GET /markets endpoint expires_at=datetime.now(timezone.utc) + timedelta(days=7), # Optional nonce=0, # For on-chain cancellation ), ) print(f"Salt: {order.salt}") print(f"Maker: {order.maker}") print(f"Expiration: {order.expiration}") # Build a MARKET order (expiration is auto-set to 5 minutes) market_order = builder.build_order( "MARKET", BuildOrderInput( side=Side.BUY, token_id="48792138471239847123984712398471239847", maker_amount=str(amounts.maker_amount), taker_amount=str(amounts.taker_amount), fee_rate_bps=100, ), ) ``` ## build_typed_data and sign_typed_data_order Build EIP-712 typed data for an order and sign it. The signature is required for submitting orders to the exchange. ```python from predict_sdk import OrderBuilder, ChainId, Side, BuildOrderInput, LimitHelperInput private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Build the order amounts = builder.get_limit_order_amounts( LimitHelperInput(side=Side.BUY, price_per_share_wei=500000000000000000, quantity_wei=10000000000000000000) ) order = builder.build_order( "LIMIT", BuildOrderInput( side=Side.BUY, token_id="48792138471239847123984712398471239847", maker_amount=str(amounts.maker_amount), taker_amount=str(amounts.taker_amount), fee_rate_bps=100, ), ) # Build typed data - specify market type typed_data = builder.build_typed_data( order, is_neg_risk=False, # True for winner-takes-all markets is_yield_bearing=False, # True for yield-bearing markets ) # Compute the typed data hash (optional - for verification) order_hash = builder.build_typed_data_hash(typed_data) print(f"Order Hash: {order_hash}") # Sign the order signed_order = builder.sign_typed_data_order(typed_data) print(f"Signature: {signed_order.signature}") print(f"Maker: {signed_order.maker}") print(f"Token ID: {signed_order.token_id}") # The signed_order can now be submitted to the Predict.fun API ``` ## sign_predict_account_message Sign arbitrary messages using a Predict Account (Kernel smart wallet). Useful for authentication or custom message signing that can be verified on-chain. ```python from predict_sdk import OrderBuilder, ChainId, OrderBuilderOptions private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make( ChainId.BNB_MAINNET, private_key, OrderBuilderOptions(predict_account="0x742d35Cc6634C0532925a3b844Bc9e7595f1E123"), ) # Sign a string message signature = builder.sign_predict_account_message("Hello, Predict.fun!") print(f"Signature: {signature}") # Output format: 0x01 + validator_address + ecdsa_signature # Sign a pre-computed hash (useful for EIP-712 typed data) raw_hash = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" signature = builder.sign_predict_account_message({"raw": raw_hash}) print(f"Raw Hash Signature: {signature}") # Async version import asyncio async def sign_async(): signature = await builder.sign_predict_account_message_async("Async message") return signature signature = asyncio.run(sign_async()) ``` ## set_approvals Set all necessary ERC-20 and ERC-1155 approvals for trading on the CTF Exchange. Must be called before placing orders. ```python from predict_sdk import OrderBuilder, ChainId private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Set all approvals at once (standard and NegRisk markets) result = builder.set_approvals(is_yield_bearing=False) if result.success: print("All approvals set successfully!") else: print("Some approvals failed:") for tx in result.transactions: if not tx.success: print(f" Failed: {tx.cause}") # For yield-bearing markets result_yb = builder.set_approvals(is_yield_bearing=True) # Or set individual approvals builder.set_ctf_exchange_approval(is_neg_risk=False, is_yield_bearing=False) builder.set_ctf_exchange_approval(is_neg_risk=True, is_yield_bearing=False) builder.set_ctf_exchange_allowance(is_neg_risk=False, is_yield_bearing=False) builder.set_ctf_exchange_allowance(is_neg_risk=True, is_yield_bearing=False) builder.set_neg_risk_adapter_approval(is_yield_bearing=False) # Async version import asyncio async def set_approvals_async(): result = await builder.set_approvals_async(is_yield_bearing=False) return result result = asyncio.run(set_approvals_async()) ``` ## balance_of Query the USDT balance for an address. Defaults to the signer's address or Predict Account. ```python from predict_sdk import OrderBuilder, ChainId private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Get signer's USDT balance balance = builder.balance_of("USDT") print(f"USDT Balance: {balance} wei") print(f"USDT Balance: {balance / 10**18} USDT") # Query another address other_balance = builder.balance_of("USDT", address="0x742d35Cc6634C0532925a3b844Bc9e7595f1E123") print(f"Other Balance: {other_balance / 10**18} USDT") # Async version import asyncio async def get_balance_async(): balance = await builder.balance_of_async("USDT") return balance balance = asyncio.run(get_balance_async()) ``` ## redeem_positions Redeem winning positions after a market resolves. For NegRisk markets, an amount must be specified. ```python from predict_sdk import OrderBuilder, ChainId private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Redeem positions for a standard market result = builder.redeem_positions( condition_id="0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", index_set=1, # 1 for YES outcome, 2 for NO outcome is_neg_risk=False, is_yield_bearing=False, ) if result.success: print("Positions redeemed successfully!") print(f"Transaction hash: {result.receipt['transactionHash'].hex()}") else: print(f"Redemption failed: {result.cause}") # Redeem positions for NegRisk (winner-takes-all) market result = builder.redeem_positions( condition_id="0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", index_set=1, amount=1000000000000000000, # 1 token - REQUIRED for NegRisk is_neg_risk=True, is_yield_bearing=False, ) # Async version import asyncio async def redeem_async(): return await builder.redeem_positions_async( condition_id="0x...", index_set=1, is_neg_risk=False, is_yield_bearing=False, ) result = asyncio.run(redeem_async()) ``` ## merge_positions Merge both outcome tokens (YES and NO) back into collateral (USDT). Requires equal amounts of both positions. ```python from predict_sdk import OrderBuilder, ChainId private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Merge positions for a standard market result = builder.merge_positions( condition_id="0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", amount=1000000000000000000, # Amount of each outcome token to merge is_neg_risk=False, is_yield_bearing=False, ) if result.success: print("Positions merged successfully!") else: print(f"Merge failed: {result.cause}") # Merge positions for NegRisk market result = builder.merge_positions( condition_id="0x...", amount=5000000000000000000, # 5 tokens is_neg_risk=True, is_yield_bearing=False, ) # Async version import asyncio async def merge_async(): return await builder.merge_positions_async( condition_id="0x...", amount=1000000000000000000, is_neg_risk=False, is_yield_bearing=False, ) result = asyncio.run(merge_async()) ``` ## split_positions Split collateral (USDT) into outcome tokens (YES and NO). Creates equal amounts of both outcome tokens. ```python from predict_sdk import OrderBuilder, ChainId private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Split USDT into outcome tokens for a standard market result = builder.split_positions( condition_id="0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", amount=1000000000000000000, # 1 USDT to split is_neg_risk=False, is_yield_bearing=False, ) if result.success: print("Positions split successfully!") # Now have 1 YES token and 1 NO token else: print(f"Split failed: {result.cause}") # Split for NegRisk market result = builder.split_positions( condition_id="0x...", amount=5000000000000000000, # 5 USDT is_neg_risk=True, is_yield_bearing=False, ) # Async version import asyncio async def split_async(): return await builder.split_positions_async( condition_id="0x...", amount=1000000000000000000, is_neg_risk=False, is_yield_bearing=False, ) result = asyncio.run(split_async()) ``` ## cancel_orders Cancel one or more orders on the CTF Exchange. The orders must have been created by the same maker. ```python from predict_sdk import OrderBuilder, ChainId, CancelOrdersOptions private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Assume we have orders from previous build_order calls orders_to_cancel = [order1, order2] # List of Order objects result = builder.cancel_orders( orders=orders_to_cancel, options=CancelOrdersOptions( is_neg_risk=False, is_yield_bearing=False, with_validation=True, # Validate token IDs before canceling ), ) if result.success: print("Orders canceled successfully!") else: print(f"Cancellation failed: {result.cause}") # Async version import asyncio async def cancel_async(): return await builder.cancel_orders_async( orders=orders_to_cancel, options=CancelOrdersOptions( is_neg_risk=False, is_yield_bearing=False, ), ) result = asyncio.run(cancel_async()) ``` ## validate_token_ids Validate that token IDs are registered on the appropriate exchange contract before trading. ```python from predict_sdk import OrderBuilder, ChainId private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" builder = OrderBuilder.make(ChainId.BNB_MAINNET, private_key) # Validate token IDs for standard market token_ids = [ 48792138471239847123984712398471239847, 98712398471239847123984712398471239847, ] is_valid = builder.validate_token_ids( token_ids=token_ids, is_neg_risk=False, is_yield_bearing=False, ) if is_valid: print("All token IDs are valid for the standard exchange") else: print("Some token IDs are invalid - check is_neg_risk parameter") # Validate for NegRisk market is_valid_neg_risk = builder.validate_token_ids( token_ids=token_ids, is_neg_risk=True, is_yield_bearing=False, ) # Async version import asyncio async def validate_async(): return await builder.validate_token_ids_async( token_ids=token_ids, is_neg_risk=False, is_yield_bearing=False, ) is_valid = asyncio.run(validate_async()) ``` ## generate_order_salt Generate a random salt for order uniqueness. Called automatically by `build_order` but available for custom use. ```python from predict_sdk import generate_order_salt # Generate a random salt salt = generate_order_salt() print(f"Generated salt: {salt}") # e.g., "1847293847" # Use in custom order building from predict_sdk import BuildOrderInput, Side order_input = BuildOrderInput( side=Side.BUY, token_id="48792138471239847123984712398471239847", maker_amount="5000000000000000000", taker_amount="10000000000000000000", fee_rate_bps=100, salt=salt, # Custom salt ) ``` ## Constants and Types The SDK exports essential constants, enums, and type definitions for working with the protocol. ```python from predict_sdk import ( # Enums ChainId, # BNB_MAINNET (56), BNB_TESTNET (97) Side, # BUY (0), SELL (1) SignatureType, # EOA (0) # Constants ADDRESSES_BY_CHAIN_ID, # Contract addresses per chain RPC_URLS_BY_CHAIN_ID, # RPC endpoints per chain PROTOCOL_NAME, # "predict.fun CTF Exchange" PROTOCOL_VERSION, # "1" MAX_SALT, # 2_147_483_648 MAX_UINT256, # 2**256 - 1 ZERO_ADDRESS, # "0x" + "0" * 40 ZERO_HASH, # "0x" + "0" * 64 # Types Order, SignedOrder, BuildOrderInput, OrderAmounts, LimitHelperInput, MarketHelperInput, MarketHelperValueInput, Book, DepthLevel, # tuple[float, float] - (price, quantity) EIP712TypedData, TransactionResult, TransactionSuccess, TransactionFail, SetApprovalsResult, CancelOrdersOptions, OrderBuilderOptions, Addresses, LogLevel, # "ERROR" | "WARN" | "INFO" | "DEBUG" ) # Access contract addresses addresses = ADDRESSES_BY_CHAIN_ID[ChainId.BNB_MAINNET] print(f"CTF Exchange: {addresses.CTF_EXCHANGE}") print(f"USDT: {addresses.USDT}") print(f"NegRisk Exchange: {addresses.NEG_RISK_CTF_EXCHANGE}") # Get RPC URL rpc_url = RPC_URLS_BY_CHAIN_ID[ChainId.BNB_MAINNET] print(f"RPC: {rpc_url}") # https://bsc-dataseed.bnbchain.org/ ``` ## Error Handling The SDK provides specific exception classes for different error conditions. ```python from predict_sdk import ( OrderBuilder, ChainId, Side, LimitHelperInput, BuildOrderInput, # Errors PredictSDKError, MissingSignerError, InvalidQuantityError, InvalidExpirationError, FailedOrderSignError, FailedTypedDataEncoderError, InvalidNegRiskConfig, MakerSignerMismatchError, InvalidSignerError, ) from datetime import datetime, timezone, timedelta builder = OrderBuilder.make(ChainId.BNB_MAINNET) try: # This will raise InvalidQuantityError (quantity too small) amounts = builder.get_limit_order_amounts( LimitHelperInput( side=Side.BUY, price_per_share_wei=500000000000000000, quantity_wei=1000000000000000, # Less than 1e16 minimum ) ) except InvalidQuantityError as e: print(f"Invalid quantity: {e}") try: # This will raise MissingSignerError (no signer provided) builder_readonly = OrderBuilder.make(ChainId.BNB_MAINNET) typed_data = builder_readonly.build_typed_data(order, is_neg_risk=False, is_yield_bearing=False) signed = builder_readonly.sign_typed_data_order(typed_data) except MissingSignerError as e: print(f"Signer required: {e}") try: # This will raise InvalidExpirationError (expiration in the past) order = builder.build_order( "LIMIT", BuildOrderInput( side=Side.BUY, token_id="123", maker_amount="1000", taker_amount="1000", fee_rate_bps=100, expires_at=datetime.now(timezone.utc) - timedelta(hours=1), # Past! ), ) except InvalidExpirationError as e: print(f"Invalid expiration: {e}") # Catch all SDK errors try: # Some SDK operation... pass except PredictSDKError as e: print(f"SDK Error: {e}") ``` ## Summary The Predict SDK provides a complete toolkit for building trading applications on the Predict.fun prediction market protocol. Primary use cases include: creating and signing limit/market orders for prediction markets, managing token approvals and allowances for the exchange contracts, querying balances and validating token IDs, and handling position lifecycle operations (split, merge, redeem). The SDK supports both standard two-outcome markets and NegRisk winner-takes-all markets with optional yield-bearing features. Integration typically follows this pattern: create an `OrderBuilder` with chain ID and signer, set approvals once using `set_approvals()`, calculate order amounts using `get_limit_order_amounts()` or `get_market_order_amounts()` with orderbook data, build orders with `build_order()`, sign with `build_typed_data()` and `sign_typed_data_order()`, then submit to the Predict.fun REST API. For position management, use `redeem_positions()` after market resolution, `merge_positions()` to convert equal YES/NO tokens back to USDT, or `split_positions()` to create outcome tokens from collateral. All contract methods support both synchronous and asynchronous execution patterns for flexible integration into various application architectures.