1use crate::error::TaiError;
4use crate::ids::ObjectId;
5use serde::{Deserialize, Serialize};
6use std::str::FromStr;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum Network {
12 Testnet,
14 Mainnet,
16 Devnet,
18 Custom,
20}
21
22impl Network {
23 pub fn default_rpc_url(self) -> &'static str {
25 match self {
26 Network::Testnet => "https://fullnode.testnet.sui.io",
27 Network::Mainnet => "https://fullnode.mainnet.sui.io",
28 Network::Devnet => "https://fullnode.devnet.sui.io",
29 Network::Custom => "",
30 }
31 }
32}
33
34impl FromStr for Network {
35 type Err = TaiError;
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 match s.to_ascii_lowercase().as_str() {
38 "testnet" => Ok(Network::Testnet),
39 "mainnet" => Ok(Network::Mainnet),
40 "devnet" => Ok(Network::Devnet),
41 "custom" => Ok(Network::Custom),
42 other => Err(TaiError::Config(format!("unknown network: {other}"))),
43 }
44 }
45}
46
47#[derive(Clone, Debug, Serialize, Deserialize)]
51pub struct TaiConfig {
52 pub network: Network,
54 pub rpc_url: String,
56 pub package_id: ObjectId,
58 pub config_id: ObjectId,
60}
61
62impl TaiConfig {
63 pub fn testnet_v1() -> Self {
74 TaiConfig {
75 network: Network::Testnet,
76 rpc_url: Network::Testnet.default_rpc_url().to_string(),
77 package_id: ObjectId::from_bytes(hex_lit(
78 "c5d0d885f6c652413034d3e44a1f9a7ab6ef6d94b6e951b6ee885e2edee3a421",
79 )),
80 config_id: ObjectId::from_bytes(hex_lit(
81 "4a8bdc697738df24f01f6161af29e70136b326db072e3d7e3630b3711f673c50",
82 )),
83 }
84 }
85}
86
87const fn hex_lit(s: &str) -> [u8; 32] {
88 let bytes = s.as_bytes();
89 assert!(bytes.len() == 64, "hex literal must be 64 chars");
90 let mut out = [0u8; 32];
91 let mut i = 0;
92 while i < 32 {
93 out[i] = nibble(bytes[2 * i]) * 16 + nibble(bytes[2 * i + 1]);
94 i += 1;
95 }
96 out
97}
98
99const fn nibble(c: u8) -> u8 {
100 match c {
101 b'0'..=b'9' => c - b'0',
102 b'a'..=b'f' => 10 + (c - b'a'),
103 b'A'..=b'F' => 10 + (c - b'A'),
104 _ => panic!("invalid hex char"),
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn testnet_v1_has_known_ids() {
114 let cfg = TaiConfig::testnet_v1();
115 assert_eq!(cfg.network, Network::Testnet);
116 assert_eq!(
117 cfg.package_id.to_string(),
118 "0xc5d0d885f6c652413034d3e44a1f9a7ab6ef6d94b6e951b6ee885e2edee3a421"
119 );
120 assert_eq!(
121 cfg.config_id.to_string(),
122 "0x4a8bdc697738df24f01f6161af29e70136b326db072e3d7e3630b3711f673c50"
123 );
124 assert_eq!(cfg.rpc_url, "https://fullnode.testnet.sui.io");
125 }
126
127 #[test]
128 fn network_parses_case_insensitive() {
129 assert_eq!("Testnet".parse::<Network>().unwrap(), Network::Testnet);
130 assert_eq!("MAINNET".parse::<Network>().unwrap(), Network::Mainnet);
131 assert!("polygon".parse::<Network>().is_err());
132 }
133}