# predict-fun-sdk
Rust SDK for the [Predict.fun](https://predict.fun) prediction market API on BNB Chain.
## Features
- **Full REST API coverage** — all 30 endpoints from the [OpenAPI spec](https://api.predict.fun/docs)
- **Real-time WebSocket feeds** — orderbook snapshots, oracle prices, cross-venue data via `wss://ws.predict.fun/ws`
- **EIP-712 order signing** — native Rust via [alloy](https://github.com/alloy-rs/alloy), no ethers dependency
- **Execution pipeline** — auth → market lookup → sign → submit, with dry-run safety guard
- **All 8 exchange contracts** — mainnet + testnet × CTF/NegRisk/Yield/YieldNegRisk
- **Amount math** — `predict_limit_order_amounts` matches the official TypeScript SDK's `getLimitOrderAmounts`
## Quick Start
```rust
use predict_fun_sdk::{PredictApiClient, PredictExecutionClient, PredictExecConfig};
use predict_fun_sdk::ws::{PredictWsClient, PredictWsMessage, Topic};
use predict_fun_sdk::order::BNB_MAINNET_CHAIN_ID;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// ── Real-time WebSocket feeds (no auth needed) ──
let (ws, mut rx) = PredictWsClient::connect_mainnet().await?;
ws.subscribe(Topic::Orderbook { market_id: 45532 }).await?;
ws.subscribe(Topic::AssetPrice { feed_id: 1 }).await?; // BTC
// Process messages in background
tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
match msg {
PredictWsMessage::Orderbook(ob) => {
println!("OB #{}: {} bids, {} asks, mid={:?}",
ob.market_id, ob.bids.len(), ob.asks.len(), ob.mid());
}
PredictWsMessage::AssetPrice(p) => {
println!("BTC: ${:.2}", p.price);
}
_ => {}
}
}
});
// ── REST API (no auth needed for public data) ──
let client = PredictApiClient::new_mainnet("your-api-key")?;
let markets = client.list_markets(&[("limit", "10".to_string())]).await?;
let ob = client.get_market_orderbook(12345).await?;
// ── Authenticated order execution ──
let config = PredictExecConfig {
api_key: "your-api-key".into(),
private_key: "0x...".into(),
chain_id: BNB_MAINNET_CHAIN_ID,
live_execution: false, // dry-run — won't submit orders
fill_or_kill: true,
};
let exec = PredictExecutionClient::new(config).await?;
// Place a limit order (dry-run)
use predict_fun_sdk::{PredictLimitOrderRequest, PredictOutcome, PredictSide, PredictStrategy};
let result = exec.place_limit_order(&PredictLimitOrderRequest {
market_id: 12345,
outcome: PredictOutcome::Yes,
side: PredictSide::Buy,
price_per_share: 0.45,
quantity: 10.0,
strategy: PredictStrategy::Limit,
slippage_bps: None,
}).await?;
println!("Order hash: {}", result.prepared.signed_order.hash);
println!("Submitted: {}", result.submitted); // false in dry-run
Ok(())
}
```
## Environment Variables
| `PREDICT_API_KEY` | Yes | API key (request via [Predict Discord](https://discord.gg/predictfun)) |
| `PREDICT_PRIVATE_KEY` | Yes | Hex private key for EIP-712 signing |
| `PREDICT_LIVE_EXECUTION` | No | Set to `1` to actually submit orders (default: `0` = dry-run) |
| `PREDICT_CHAIN_ID` | No | `56` (mainnet, default) or `97` (testnet) |
| `PREDICT_FILL_OR_KILL` | No | `1` (default) or `0` |
## API Coverage
All 30 endpoints from the OpenAPI spec are covered:
**Public** — Markets, Orderbook, Timeseries, Categories, Tags, Search, Positions by address, Order matches
**JWT-gated** — Orders (create/list/cancel/get), Account, Activity, Positions, Yield pending
**OAuth** — Finalize, Orders (list/create/cancel), Positions
## WebSocket Topics
| `predictOrderbook/{marketId}` | None | On every change | Full orderbook snapshot (bids/asks, version, lastOrderSettled) |
| `assetPriceUpdate/{feedId}` | None | ~3-5/sec | Oracle price (feed 1=BTC, 4=ETH) |
| `polymarketChance/{marketId}` | None | On change | Cross-venue Polymarket probability |
| `kalshiChance/{marketId}` | None | On change | Cross-venue Kalshi probability |
| `predictWalletEvents/{jwt}` | JWT | On event | Fill/settlement notifications |
## Architecture
```
predict_fun_sdk
├── api — REST client, endpoint specs, raw/typed request helpers
├── ws — WebSocket client with auto-reconnect, heartbeat, typed messages
├── order — EIP-712 structs, signing, amount math, exchange addresses
└── execution — High-level pipeline: auth → prepare → sign → submit
```
## Exchange Contracts
| Mainnet | CTF | `0x8BC070BEdAB741406F4B1Eb65A72bee27894B689` |
| Mainnet | NegRisk | `0x365fb81bd4A24D6303cd2F19c349dE6894D8d58A` |
| Mainnet | Yield | `0x6bEb5a40C032AFc305961162d8204CDA16DECFa5` |
| Mainnet | YieldNegRisk | `0x8A289d458f5a134bA40015085A8F50Ffb681B41d` |
## References
- [Predict.fun Dev Docs](https://dev.predict.fun/)
- [OpenAPI Explorer](https://api.predict.fun/docs)
- [TypeScript SDK](https://github.com/PredictDotFun/sdk) (reference implementation)
- [Python SDK](https://github.com/PredictDotFun/sdk-python)
## License
MIT