polymarket 0.1.0

Rust SDK for Polymarket prediction market - CLOB trading, on-chain operations, and WebSocket streaming
Documentation
use alloy::primitives::Address;
use std::str::FromStr;

use crate::onchain::error::{OnchainError, Result};

/// Get contract addresses for a specific chain ID
pub fn get_addresses(chain_id: u64) -> Result<Addresses> {
    match chain_id {
        137 => Ok(Addresses::polygon_mainnet()),
        80002 => Ok(Addresses::polygon_amoy()),
        _ => Err(OnchainError::NetworkError(format!(
            "Unsupported chain ID: {}",
            chain_id
        ))),
    }
}

/// Contract addresses for a specific network
#[derive(Debug, Clone)]
pub struct Addresses {
    /// Proxy Wallet Factory - used for email/Magic accounts
    pub proxy_factory: Address,

    /// Safe (Gnosis Safe) Factory
    pub safe_factory: Address,

    /// Safe Singleton contract
    pub safe_singleton: Address,

    /// USDC token address
    pub usdc: Address,

    /// Conditional Tokens Framework (CTF) - main contract for split/merge
    pub ctf: Address,

    /// Negative Risk Adapter - for certain market types
    pub neg_risk_adapter: Address,

    /// CTF Exchange - for trading on the CLOB
    pub exchange: Address,

    /// Neg Risk CTF Exchange
    pub neg_risk_exchange: Address,
}

impl Addresses {
    /// Polygon mainnet (Chain ID: 137)
    pub fn polygon_mainnet() -> Self {
        Self {
            proxy_factory: parse("0xaB45c5A4B0c941a2F231C04C3f49182e1A254052"),
            safe_factory: parse("0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2"),
            safe_singleton: parse("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
            usdc: parse("0x2791bca1f2de4661ed88a30c99a7a9449aa84174"), // USDC.e - Polymarket uses this exclusively
            ctf: parse("0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"),
            neg_risk_adapter: parse("0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"),
            exchange: parse("0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"),
            neg_risk_exchange: parse("0xC5d563A36AE78145C45a50134d48A1215220f80a"),
        }
    }

    /// Polygon Amoy testnet (Chain ID: 80002)
    pub fn polygon_amoy() -> Self {
        Self {
            proxy_factory: parse("0x0000000000000000000000000000000000000000"), // TODO: Update when available
            safe_factory: parse("0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2"), // Same as mainnet
            safe_singleton: parse("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552"), // Same as mainnet
            usdc: parse("0x9c4e1703476e875070ee25b56a58b008cfb8fa78"), // Test USDC
            ctf: parse("0x69308FB512518e39F9b16112fA8d994F4e2Bf8bB"),
            neg_risk_adapter: parse("0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"),
            exchange: parse("0xdFE02Eb6733538f8Ea35D585af8DE5958AD99E40"),
            neg_risk_exchange: parse("0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"), // Same as adapter on testnet
        }
    }
}

/// Helper to parse address (panics on invalid - these are hardcoded constants)
fn parse(addr: &str) -> Address {
    Address::from_str(addr).expect("Invalid hardcoded address")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_mainnet_addresses() {
        let addrs = get_addresses(137).unwrap();
        assert_eq!(addrs.ctf, parse("0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"));
    }

    #[test]
    fn test_get_testnet_addresses() {
        let addrs = get_addresses(80002).unwrap();
        assert_eq!(addrs.ctf, parse("0x69308FB512518e39F9b16112fA8d994F4e2Bf8bB"));
    }

    #[test]
    fn test_unsupported_chain() {
        let result = get_addresses(999);
        assert!(result.is_err());
    }

    #[test]
    fn test_mainnet_usdc_address() {
        let addrs = Addresses::polygon_mainnet();
        // Mainnet uses USDC.e
        assert_eq!(addrs.usdc, parse("0x2791bca1f2de4661ed88a30c99a7a9449aa84174"));
    }

    #[test]
    fn test_testnet_usdc_address() {
        let addrs = Addresses::polygon_amoy();
        // Testnet uses test USDC
        assert_eq!(addrs.usdc, parse("0x9c4e1703476e875070ee25b56a58b008cfb8fa78"));
    }
}