ostium-rust-sdk 0.1.0

Rust SDK for interacting with the Ostium trading platform on Arbitrum
Documentation
//! Network configuration for the Ostium SDK
//!
//! This module provides network-specific configuration including RPC URLs,
//! chain IDs, contract addresses, and GraphQL endpoints.

use alloy_primitives::Address;
use serde::{Deserialize, Serialize};
use url::Url;

use crate::error::{OstiumError, Result};

/// Supported networks for the Ostium platform
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Network {
    /// Arbitrum mainnet
    Mainnet,
    /// Arbitrum testnet (Sepolia)
    Testnet,
}

impl Network {
    /// Get the configuration for this network
    pub fn config(self) -> NetworkConfig {
        match self {
            Network::Mainnet => NetworkConfig::mainnet(),
            Network::Testnet => NetworkConfig::testnet(),
        }
    }
}

/// Network configuration containing all necessary endpoints and addresses
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
    /// Network identifier
    pub network: Network,
    /// RPC URL for the network
    pub rpc_url: Url,
    /// Chain ID
    pub chain_id: u64,
    /// GraphQL API endpoint
    pub graphql_url: Url,
    /// Trading contract address
    pub trading_contract: Address,
    /// Storage contract address
    pub storage_contract: Address,
    /// Price feed contract address
    pub price_feed_contract: Address,
    /// USDC token address
    pub usdc_address: Address,
}

impl NetworkConfig {
    /// Create a mainnet configuration
    pub fn mainnet() -> Self {
        Self {
            network: Network::Mainnet,
            chain_id: 42161,
            rpc_url: "https://arb1.arbitrum.io/rpc".parse().expect("Valid URL"),
            graphql_url: "https://subgraph.satsuma-prod.com/391a61815d32/ostium/ost-prod/api"
                .parse()
                .expect("Valid URL"),
            usdc_address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
                .parse()
                .expect("Valid address"),
            trading_contract: "0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411"
                .parse()
                .expect("Valid address"),
            storage_contract: "0xcCd5891083A8acD2074690F65d3024E7D13d66E7"
                .parse()
                .expect("Valid address"),
            price_feed_contract: "0x0000000000000000000000000000000000000003"
                .parse()
                .expect("Valid address"),
        }
    }

    /// Create a testnet configuration
    pub fn testnet() -> Self {
        Self {
            network: Network::Testnet,
            chain_id: 421614,
            rpc_url: "https://sepolia-rollup.arbitrum.io/rpc"
                .parse()
                .expect("Valid URL"),
            graphql_url: "https://subgraph.satsuma-prod.com/391a61815d32/ostium/ost-sep-final/api"
                .parse()
                .expect("Valid URL"),
            usdc_address: "0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548"
                .parse()
                .expect("Valid address"),
            trading_contract: "0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe"
                .parse()
                .expect("Valid address"),
            storage_contract: "0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8"
                .parse()
                .expect("Valid address"),
            price_feed_contract: "0x0000000000000000000000000000000000000003"
                .parse()
                .expect("Valid address"),
        }
    }

    /// Create a custom network configuration
    pub fn custom(
        rpc_url: Url,
        chain_id: u64,
        graphql_url: Url,
        trading_contract: Address,
        storage_contract: Address,
        price_feed_contract: Address,
        usdc_address: Address,
    ) -> Self {
        Self {
            network: Network::Mainnet, // Default to mainnet for custom
            rpc_url,
            chain_id,
            graphql_url,
            trading_contract,
            storage_contract,
            price_feed_contract,
            usdc_address,
        }
    }

    /// Validate the configuration
    pub fn validate(&self) -> Result<()> {
        // Ensure chain ID matches expected values
        match self.network {
            Network::Mainnet if self.chain_id != 42161 => {
                return Err(OstiumError::config("Invalid chain ID for mainnet"));
            }
            Network::Testnet if self.chain_id != 421614 => {
                return Err(OstiumError::config("Invalid chain ID for testnet"));
            }
            _ => {}
        }

        // Ensure no zero addresses
        if self.trading_contract == Address::ZERO {
            return Err(OstiumError::config(
                "Trading contract address cannot be zero",
            ));
        }
        if self.storage_contract == Address::ZERO {
            return Err(OstiumError::config(
                "Storage contract address cannot be zero",
            ));
        }
        if self.price_feed_contract == Address::ZERO {
            return Err(OstiumError::config(
                "Price feed contract address cannot be zero",
            ));
        }
        if self.usdc_address == Address::ZERO {
            return Err(OstiumError::config("USDC address cannot be zero"));
        }

        Ok(())
    }
}

/// Builder for creating custom network configurations
pub struct NetworkConfigBuilder {
    config: NetworkConfig,
}

impl NetworkConfigBuilder {
    /// Create a new builder starting from a base network
    pub fn new(network: Network) -> Self {
        Self {
            config: network.config(),
        }
    }

    /// Set a custom RPC URL
    pub fn with_rpc_url(mut self, url: Url) -> Self {
        self.config.rpc_url = url;
        self
    }

    /// Set a custom GraphQL URL
    pub fn with_graphql_url(mut self, url: Url) -> Self {
        self.config.graphql_url = url;
        self
    }

    /// Set custom contract addresses
    pub fn with_contracts(
        mut self,
        trading: Address,
        storage: Address,
        price_feed: Address,
    ) -> Self {
        self.config.trading_contract = trading;
        self.config.storage_contract = storage;
        self.config.price_feed_contract = price_feed;
        self
    }

    /// Build and validate the configuration
    pub fn build(self) -> Result<NetworkConfig> {
        self.config.validate()?;
        Ok(self.config)
    }
}