Skip to main content

csv_adapter_sui/
config.rs

1//! Sui adapter configuration
2//!
3//! This module provides comprehensive configuration for the Sui adapter,
4//! including network selection, checkpoint settings, and transaction parameters.
5
6use serde::{Deserialize, Serialize};
7
8/// Sui network type
9#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
10pub enum SuiNetwork {
11    /// Sui mainnet
12    Mainnet,
13    /// Sui testnet
14    Testnet,
15    /// Sui devnet
16    Devnet,
17    /// Local network
18    Local,
19    /// Custom network with user-defined chain ID
20    Custom { chain_id: String },
21}
22
23impl SuiNetwork {
24    /// Returns the default RPC URL for this network.
25    pub fn default_rpc_url(&self) -> &str {
26        match self {
27            SuiNetwork::Mainnet => "https://fullnode.mainnet.sui.io:443",
28            SuiNetwork::Testnet => "https://fullnode.testnet.sui.io:443",
29            SuiNetwork::Devnet => "https://fullnode.devnet.sui.io:443",
30            SuiNetwork::Local => "http://127.0.0.1:9000",
31            SuiNetwork::Custom { .. } => "",
32        }
33    }
34
35    /// Returns the chain ID for this network.
36    pub fn chain_id(&self) -> &str {
37        match self {
38            SuiNetwork::Mainnet => "mainnet",
39            SuiNetwork::Testnet => "testnet",
40            SuiNetwork::Devnet => "devnet",
41            SuiNetwork::Local => "local",
42            SuiNetwork::Custom { chain_id } => chain_id.as_str(),
43        }
44    }
45}
46
47/// Configuration for checkpoint verification.
48#[derive(Clone, Debug, Serialize, Deserialize)]
49pub struct CheckpointConfig {
50    /// Whether to require certified checkpoints.
51    pub require_certified: bool,
52    /// Maximum number of epochs to look back for checkpoint verification.
53    pub max_epoch_lookback: u64,
54    /// Timeout for checkpoint verification in milliseconds.
55    pub timeout_ms: u64,
56}
57
58impl Default for CheckpointConfig {
59    fn default() -> Self {
60        Self {
61            require_certified: true,
62            max_epoch_lookback: 5,
63            timeout_ms: 30_000,
64        }
65    }
66}
67
68/// Configuration for transaction submission.
69#[derive(Clone, Debug, Serialize, Deserialize)]
70pub struct TransactionConfig {
71    /// Maximum gas budget for transactions (in MIST).
72    pub max_gas_budget: u64,
73    /// Maximum gas price (in MIST).
74    pub max_gas_price: u64,
75    /// Timeout for transaction confirmation in milliseconds.
76    pub confirmation_timeout_ms: u64,
77    /// Number of retries for failed transactions.
78    pub max_retries: u32,
79}
80
81impl Default for TransactionConfig {
82    fn default() -> Self {
83        Self {
84            max_gas_budget: 1_000_000_000, // 1 SUI
85            max_gas_price: 1_000,
86            confirmation_timeout_ms: 60_000,
87            max_retries: 3,
88        }
89    }
90}
91
92/// Configuration for the CSV seal contract.
93#[derive(Clone, Debug, Serialize, Deserialize)]
94pub struct SealContractConfig {
95    /// Package ID where CSVSeal module is deployed.
96    /// Must be set explicitly — there is no safe default.
97    pub package_id: Option<String>,
98    /// Module name (typically "csv_seal").
99    pub module_name: String,
100    /// Seal object type name.
101    pub seal_type: String,
102}
103
104impl Default for SealContractConfig {
105    fn default() -> Self {
106        Self {
107            package_id: None, // No safe default — must be set explicitly
108            module_name: "csv_seal".to_string(),
109            seal_type: "Seal".to_string(),
110        }
111    }
112}
113
114/// Configuration for the Sui anchor layer.
115#[derive(Clone, Debug, Serialize, Deserialize)]
116pub struct SuiConfig {
117    /// Sui network (mainnet, testnet, devnet, local).
118    pub network: SuiNetwork,
119    /// RPC endpoint URL.
120    pub rpc_url: String,
121    /// Checkpoint verification configuration.
122    pub checkpoint: CheckpointConfig,
123    /// Transaction submission configuration.
124    pub transaction: TransactionConfig,
125    /// CSV seal contract configuration.
126    pub seal_contract: SealContractConfig,
127}
128
129impl SuiConfig {
130    /// Create a new configuration for the specified network.
131    pub fn new(network: SuiNetwork) -> Self {
132        let rpc_url = network.default_rpc_url().to_string();
133        Self {
134            network,
135            rpc_url,
136            checkpoint: CheckpointConfig::default(),
137            transaction: TransactionConfig::default(),
138            seal_contract: SealContractConfig::default(),
139        }
140    }
141
142    /// Returns the chain ID for the configured network.
143    pub fn chain_id(&self) -> &str {
144        self.network.chain_id()
145    }
146
147    /// Validate the configuration.
148    pub fn validate(&self) -> Result<(), String> {
149        if self.rpc_url.is_empty() {
150            return Err("RPC URL cannot be empty".to_string());
151        }
152        match &self.seal_contract.package_id {
153            Some(id) if id.is_empty() => {
154                return Err("Seal contract package ID cannot be empty".to_string())
155            }
156            None => {
157                return Err(
158                    "Seal contract package ID must be set — deploy the contract first".to_string(),
159                )
160            }
161            _ => {}
162        }
163        if self.transaction.max_gas_budget == 0 {
164            return Err("Max gas budget must be greater than 0".to_string());
165        }
166        if self.checkpoint.timeout_ms == 0 {
167            return Err("Checkpoint timeout must be greater than 0".to_string());
168        }
169        Ok(())
170    }
171}
172
173impl Default for SuiConfig {
174    fn default() -> Self {
175        Self::new(SuiNetwork::Testnet)
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    #[test]
184    fn test_default_config() {
185        let config = SuiConfig::default();
186        assert_eq!(config.network, SuiNetwork::Testnet);
187        assert_eq!(config.rpc_url, "https://fullnode.testnet.sui.io:443");
188    }
189
190    #[test]
191    fn test_config_custom_rpc() {
192        let config = SuiConfig::new(SuiNetwork::Mainnet);
193        assert_eq!(config.network, SuiNetwork::Mainnet);
194        assert_eq!(config.rpc_url, "https://fullnode.mainnet.sui.io:443");
195    }
196
197    #[test]
198    fn test_config_validation() {
199        let mut config = SuiConfig::default();
200        config.seal_contract.package_id = Some("0x1234".to_string());
201        assert!(config.validate().is_ok());
202
203        config.rpc_url = "".to_string();
204        assert!(config.validate().is_err());
205    }
206
207    #[test]
208    fn test_network_chain_ids() {
209        assert_eq!(SuiNetwork::Mainnet.chain_id(), "mainnet");
210        assert_eq!(SuiNetwork::Testnet.chain_id(), "testnet");
211        assert_eq!(SuiNetwork::Devnet.chain_id(), "devnet");
212    }
213
214    #[test]
215    fn test_invalid_config() {
216        let mut config = SuiConfig::default();
217        config.seal_contract.package_id = Some("".to_string());
218        assert!(config.validate().is_err());
219    }
220}