predict-sdk 0.1.0

Rust SDK for Predict.fun prediction market - order building, EIP-712 signing, and real-time WebSocket data
Documentation
use crate::types::ChainId;
use once_cell::sync::Lazy;
use std::collections::HashMap;

/// Maximum salt value for order entropy
pub const MAX_SALT: u64 = 2_147_483_648;

/// Five minutes in seconds
pub const FIVE_MINUTES_SECONDS: u64 = 60 * 5;

/// Protocol name for EIP-712 domain
pub const PROTOCOL_NAME: &str = "predict.fun CTF Exchange";

/// Protocol version for EIP-712 domain
pub const PROTOCOL_VERSION: &str = "1";

/// Contract addresses for BNB Mainnet
pub mod mainnet {
    pub const YIELD_BEARING_CTF_EXCHANGE: &str = "0x6bEb5a40C032AFc305961162d8204CDA16DECFa5";
    pub const YIELD_BEARING_NEG_RISK_CTF_EXCHANGE: &str = "0x8A289d458f5a134bA40015085A8F50Ffb681B41d";
    pub const YIELD_BEARING_NEG_RISK_ADAPTER: &str = "0x41dCe1A4B8FB5e6327701750aF6231B7CD0B2A40";
    pub const YIELD_BEARING_CONDITIONAL_TOKENS: &str = "0x9400F8Ad57e9e0F352345935d6D3175975eb1d9F";
    pub const YIELD_BEARING_NEG_RISK_CONDITIONAL_TOKENS: &str = "0xF64b0b318AAf83BD9071110af24D24445719A07F";

    pub const CTF_EXCHANGE: &str = "0x8BC070BEdAB741406F4B1Eb65A72bee27894B689";
    pub const NEG_RISK_CTF_EXCHANGE: &str = "0x365fb81bd4A24D6303cd2F19c349dE6894D8d58A";
    pub const NEG_RISK_ADAPTER: &str = "0xc3Cf7c252f65E0d8D88537dF96569AE94a7F1A6E";
    pub const CONDITIONAL_TOKENS: &str = "0x22DA1810B194ca018378464a58f6Ac2B10C9d244";
    pub const NEG_RISK_CONDITIONAL_TOKENS: &str = "0x22DA1810B194ca018378464a58f6Ac2B10C9d244";

    pub const USDT: &str = "0x55d398326f99059fF775485246999027B3197955";
    pub const KERNEL: &str = "0xBAC849bB641841b44E965fB01A4Bf5F074f84b4D";
    pub const ECDSA_VALIDATOR: &str = "0x845ADb2C711129d4f3966735eD98a9F09fC4cE57";
}

/// Contract addresses for BNB Testnet
pub mod testnet {
    pub const YIELD_BEARING_CTF_EXCHANGE: &str = "0x8a6B4Fa700A1e310b106E7a48bAFa29111f66e89";
    pub const YIELD_BEARING_NEG_RISK_CTF_EXCHANGE: &str = "0x95D5113bc50eD201e319101bbca3e0E250662fCC";
    pub const YIELD_BEARING_NEG_RISK_ADAPTER: &str = "0xb74aea04bdeBE912Aa425bC9173F9668e6f11F99";
    pub const YIELD_BEARING_CONDITIONAL_TOKENS: &str = "0x38BF1cbD66d174bb5F3037d7068E708861D68D7f";
    pub const YIELD_BEARING_NEG_RISK_CONDITIONAL_TOKENS: &str = "0x26e865CbaAe99b62fbF9D18B55c25B5E079A93D5";

    pub const CTF_EXCHANGE: &str = "0x2A6413639BD3d73a20ed8C95F634Ce198ABbd2d7";
    pub const NEG_RISK_CTF_EXCHANGE: &str = "0xd690b2bd441bE36431F6F6639D7Ad351e7B29680";
    pub const NEG_RISK_ADAPTER: &str = "0x285c1B939380B130D7EBd09467b93faD4BA623Ed";
    pub const CONDITIONAL_TOKENS: &str = "0x2827AAef52D71910E8FBad2FfeBC1B6C2DA37743";
    pub const NEG_RISK_CONDITIONAL_TOKENS: &str = "0x2827AAef52D71910E8FBad2FfeBC1B6C2DA37743";

    pub const USDT: &str = "0xB32171ecD878607FFc4F8FC0bCcE6852BB3149E0";
    pub const KERNEL: &str = "0xBAC849bB641841b44E965fB01A4Bf5F074f84b4D";
    pub const ECDSA_VALIDATOR: &str = "0x845ADb2C711129d4f3966735eD98a9F09fC4cE57";
}

/// All contract addresses organized by contract type and chain
#[derive(Debug, Clone)]
pub struct Addresses {
    pub yield_bearing_ctf_exchange: &'static str,
    pub yield_bearing_neg_risk_ctf_exchange: &'static str,
    pub yield_bearing_neg_risk_adapter: &'static str,
    pub yield_bearing_conditional_tokens: &'static str,
    pub yield_bearing_neg_risk_conditional_tokens: &'static str,

    pub ctf_exchange: &'static str,
    pub neg_risk_ctf_exchange: &'static str,
    pub neg_risk_adapter: &'static str,
    pub conditional_tokens: &'static str,
    pub neg_risk_conditional_tokens: &'static str,

    pub usdt: &'static str,
    pub kernel: &'static str,
    pub ecdsa_validator: &'static str,
}

impl Addresses {
    pub fn for_chain(chain_id: ChainId) -> Self {
        match chain_id {
            ChainId::BnbMainnet => Self {
                yield_bearing_ctf_exchange: mainnet::YIELD_BEARING_CTF_EXCHANGE,
                yield_bearing_neg_risk_ctf_exchange: mainnet::YIELD_BEARING_NEG_RISK_CTF_EXCHANGE,
                yield_bearing_neg_risk_adapter: mainnet::YIELD_BEARING_NEG_RISK_ADAPTER,
                yield_bearing_conditional_tokens: mainnet::YIELD_BEARING_CONDITIONAL_TOKENS,
                yield_bearing_neg_risk_conditional_tokens: mainnet::YIELD_BEARING_NEG_RISK_CONDITIONAL_TOKENS,

                ctf_exchange: mainnet::CTF_EXCHANGE,
                neg_risk_ctf_exchange: mainnet::NEG_RISK_CTF_EXCHANGE,
                neg_risk_adapter: mainnet::NEG_RISK_ADAPTER,
                conditional_tokens: mainnet::CONDITIONAL_TOKENS,
                neg_risk_conditional_tokens: mainnet::NEG_RISK_CONDITIONAL_TOKENS,

                usdt: mainnet::USDT,
                kernel: mainnet::KERNEL,
                ecdsa_validator: mainnet::ECDSA_VALIDATOR,
            },
            ChainId::BnbTestnet => Self {
                yield_bearing_ctf_exchange: testnet::YIELD_BEARING_CTF_EXCHANGE,
                yield_bearing_neg_risk_ctf_exchange: testnet::YIELD_BEARING_NEG_RISK_CTF_EXCHANGE,
                yield_bearing_neg_risk_adapter: testnet::YIELD_BEARING_NEG_RISK_ADAPTER,
                yield_bearing_conditional_tokens: testnet::YIELD_BEARING_CONDITIONAL_TOKENS,
                yield_bearing_neg_risk_conditional_tokens: testnet::YIELD_BEARING_NEG_RISK_CONDITIONAL_TOKENS,

                ctf_exchange: testnet::CTF_EXCHANGE,
                neg_risk_ctf_exchange: testnet::NEG_RISK_CTF_EXCHANGE,
                neg_risk_adapter: testnet::NEG_RISK_ADAPTER,
                conditional_tokens: testnet::CONDITIONAL_TOKENS,
                neg_risk_conditional_tokens: testnet::NEG_RISK_CONDITIONAL_TOKENS,

                usdt: testnet::USDT,
                kernel: testnet::KERNEL,
                ecdsa_validator: testnet::ECDSA_VALIDATOR,
            },
        }
    }

    /// Get the CTF Exchange address based on market type
    pub fn get_ctf_exchange(&self, is_yield_bearing: bool, is_neg_risk: bool) -> &'static str {
        match (is_yield_bearing, is_neg_risk) {
            (true, true) => self.yield_bearing_neg_risk_ctf_exchange,
            (true, false) => self.yield_bearing_ctf_exchange,
            (false, true) => self.neg_risk_ctf_exchange,
            (false, false) => self.ctf_exchange,
        }
    }

    /// Get the Conditional Tokens address based on market type
    pub fn get_conditional_tokens(&self, is_yield_bearing: bool, is_neg_risk: bool) -> &'static str {
        match (is_yield_bearing, is_neg_risk) {
            (true, true) => self.yield_bearing_neg_risk_conditional_tokens,
            (true, false) => self.yield_bearing_conditional_tokens,
            (false, true) => self.neg_risk_conditional_tokens,
            (false, false) => self.conditional_tokens,
        }
    }
}

/// RPC URLs by chain
pub static RPC_URLS: Lazy<HashMap<ChainId, &'static str>> = Lazy::new(|| {
    let mut map = HashMap::new();
    map.insert(ChainId::BnbMainnet, "https://bsc-dataseed.bnbchain.org/");
    map.insert(ChainId::BnbTestnet, "https://bsc-testnet-dataseed.bnbchain.org/");
    map
});

/// EIP-712 Order structure definition
pub const ORDER_STRUCTURE: &[(&str, &str)] = &[
    ("salt", "uint256"),
    ("maker", "address"),
    ("signer", "address"),
    ("taker", "address"),
    ("tokenId", "uint256"),
    ("makerAmount", "uint256"),
    ("takerAmount", "uint256"),
    ("expiration", "uint256"),
    ("nonce", "uint256"),
    ("feeRateBps", "uint256"),
    ("side", "uint8"),
    ("signatureType", "uint8"),
];

/// Zero address constant
pub const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000";