openzeppelin_relayer/models/network/stellar/
network.rs

1use crate::models::{NetworkConfigData, NetworkRepoModel, RepositoryError};
2use core::time::Duration;
3use serde::{Deserialize, Serialize};
4use sha2::{Digest, Sha256};
5use soroban_rs::xdr::Hash;
6
7#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
8pub struct StellarNetwork {
9    /// Unique network identifier (e.g., "mainnet", "sepolia", "custom-devnet").
10    pub network: String,
11    /// List of RPC endpoint URLs for connecting to the network.
12    pub rpc_urls: Vec<String>,
13    /// List of Explorer endpoint URLs for connecting to the network.
14    pub explorer_urls: Option<Vec<String>>,
15    /// Estimated average time between blocks in milliseconds.
16    pub average_blocktime_ms: u64,
17    /// Flag indicating if the network is a testnet.
18    pub is_testnet: bool,
19    /// List of arbitrary tags for categorizing or filtering networks.
20    pub tags: Vec<String>,
21    /// The passphrase for the Stellar network.
22    pub passphrase: String,
23}
24
25impl TryFrom<NetworkRepoModel> for StellarNetwork {
26    type Error = RepositoryError;
27
28    /// Converts a NetworkRepoModel to a StellarNetwork.
29    ///
30    /// # Arguments
31    /// * `network_repo` - The repository model to convert
32    ///
33    /// # Returns
34    /// Result containing the StellarNetwork if successful, or a RepositoryError
35    fn try_from(network_repo: NetworkRepoModel) -> Result<Self, Self::Error> {
36        match &network_repo.config {
37            NetworkConfigData::Stellar(stellar_config) => {
38                let common = &stellar_config.common;
39
40                let rpc_urls = common.rpc_urls.clone().ok_or_else(|| {
41                    RepositoryError::InvalidData(format!(
42                        "Stellar network '{}' has no rpc_urls",
43                        network_repo.name
44                    ))
45                })?;
46
47                let average_blocktime_ms = common.average_blocktime_ms.ok_or_else(|| {
48                    RepositoryError::InvalidData(format!(
49                        "Stellar network '{}' has no average_blocktime_ms",
50                        network_repo.name
51                    ))
52                })?;
53
54                let passphrase = stellar_config.passphrase.clone().ok_or_else(|| {
55                    RepositoryError::InvalidData(format!(
56                        "Stellar network '{}' has no passphrase",
57                        network_repo.name
58                    ))
59                })?;
60
61                Ok(StellarNetwork {
62                    network: common.network.clone(),
63                    rpc_urls,
64                    explorer_urls: common.explorer_urls.clone(),
65                    average_blocktime_ms,
66                    is_testnet: common.is_testnet.unwrap_or(false),
67                    tags: common.tags.clone().unwrap_or_default(),
68                    passphrase,
69                })
70            }
71            _ => Err(RepositoryError::InvalidData(format!(
72                "Network '{}' is not a Stellar network",
73                network_repo.name
74            ))),
75        }
76    }
77}
78
79impl StellarNetwork {
80    pub fn network_id(&self) -> Hash {
81        let passphrase = self.passphrase.clone();
82        let hash_bytes: [u8; 32] = Sha256::digest(passphrase.as_bytes()).into();
83        Hash(hash_bytes)
84    }
85
86    pub fn average_blocktime(&self) -> Option<Duration> {
87        Some(Duration::from_millis(self.average_blocktime_ms))
88    }
89
90    pub fn public_rpc_urls(&self) -> Option<&[String]> {
91        if self.rpc_urls.is_empty() {
92            None
93        } else {
94            Some(&self.rpc_urls)
95        }
96    }
97
98    pub fn explorer_urls(&self) -> Option<&[String]> {
99        self.explorer_urls.as_deref()
100    }
101
102    pub fn is_testnet(&self) -> bool {
103        self.is_testnet
104    }
105}