polymarket 0.1.0

Rust SDK for Polymarket prediction market - CLOB trading, on-chain operations, and WebSocket streaming
Documentation
//! RPC provider management for Polygon network interactions

use alloy::providers::{Provider, ProviderBuilder};
use alloy::network::AnyNetwork;
use alloy::primitives::U256;

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

/// Wrapper around Alloy's Provider for Polygon RPC interactions
///
/// In Alloy 1.0, we use AnyNetwork to handle different network types flexibly
#[derive(Clone)]
pub struct OnchainProvider {
    pub(crate) provider: alloy::providers::DynProvider<AnyNetwork>,
    pub(crate) network: NetworkConfig,
}

impl OnchainProvider {
    /// Create a new provider from a NetworkConfig
    ///
    /// # Example
    /// ```no_run
    /// # use polymarket_sdk::onchain::{NetworkConfig, OnchainProvider};
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let config = NetworkConfig::polygon_mainnet();
    /// let provider = OnchainProvider::new(&config)?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new(network: &NetworkConfig) -> Result<Self> {
        let url = network.rpc_url.parse().map_err(|e| {
            OnchainError::NetworkError(format!("Invalid RPC URL: {}", e))
        })?;

        // TODO: Add retry layer using alloy_transport::RetryBackoffService
        // See: https://alloy.rs/examples/layers/retry_layer
        let provider = ProviderBuilder::new()
            .network::<AnyNetwork>()
            .connect_http(url);

        Ok(Self {
            provider: provider.erased(),
            network: network.clone(),
        })
    }


    /// Get the underlying Alloy provider
    pub fn provider(&self) -> &alloy::providers::DynProvider<AnyNetwork> {
        &self.provider
    }

    /// Get the network configuration
    pub fn network(&self) -> &NetworkConfig {
        &self.network
    }

    /// Get the chain ID
    pub fn chain_id(&self) -> u64 {
        self.network.chain_id
    }

    /// Get the current gas price
    pub async fn get_gas_price(&self) -> Result<U256> {
        let gas_price = self.provider
            .get_gas_price()
            .await
            .map_err(|e| OnchainError::NetworkError(format!("Failed to get gas price: {}", e)))?;

        Ok(U256::from(gas_price))
    }

    /// Get the latest block number
    pub async fn get_block_number(&self) -> Result<u64> {
        self.provider
            .get_block_number()
            .await
            .map_err(|e| OnchainError::NetworkError(format!("Failed to get block number: {}", e)))
    }

    /// Check if the provider is connected and responsive
    pub async fn is_connected(&self) -> bool {
        self.provider.get_chain_id().await.is_ok()
    }
}

impl std::fmt::Debug for OnchainProvider {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("OnchainProvider")
            .field("chain_id", &self.network.chain_id)
            .field("rpc_url", &self.network.rpc_url)
            .finish()
    }
}

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

    #[test]
    fn test_provider_debug() {
        let network = NetworkConfig::polygon_mainnet();
        let url = network.rpc_url.parse().unwrap();
        let provider = ProviderBuilder::new()
            .network::<AnyNetwork>()
            .connect_http(url)
            .erased();

        let provider = OnchainProvider {
            provider,
            network,
        };

        let debug_str = format!("{:?}", provider);
        assert!(debug_str.contains("OnchainProvider"));
        assert!(debug_str.contains("137"));
    }

    #[test]
    fn test_chain_id() {
        let network = NetworkConfig::polygon_mainnet();
        let url = network.rpc_url.parse().unwrap();
        let provider = ProviderBuilder::new()
            .network::<AnyNetwork>()
            .connect_http(url)
            .erased();

        let provider = OnchainProvider {
            provider,
            network: network.clone(),
        };

        assert_eq!(provider.chain_id(), 137);
        assert_eq!(provider.network().chain_id, 137);
    }
}