Skip to main content

aptos_sdk/types/
chain_id.rs

1//! Chain ID type.
2//!
3//! The chain ID identifies which Aptos network a transaction is intended for,
4//! preventing replay attacks across networks.
5
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9/// A chain identifier for an Aptos network.
10///
11/// The chain ID is included in every transaction to ensure transactions
12/// cannot be replayed across different networks.
13///
14/// # Known Chain IDs
15///
16/// - Mainnet: 1
17/// - Testnet: 2
18/// - Devnet: not fixed -- devnet is reset periodically and its chain ID
19///   changes on each reset, so it must be fetched from the network rather
20///   than hardcoded.
21/// - Localnet: typically 4, but depends on how the local node was configured.
22///
23/// Note: [`ChainId::default()`] returns testnet (`2`), not a local or devnet
24/// value.
25///
26/// # Example
27///
28/// ```rust
29/// use aptos_sdk::ChainId;
30///
31/// let mainnet = ChainId::mainnet();
32/// assert_eq!(mainnet.id(), 1);
33///
34/// let custom = ChainId::new(42);
35/// assert_eq!(custom.id(), 42);
36/// ```
37#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
38#[serde(transparent)]
39pub struct ChainId(u8);
40
41impl ChainId {
42    /// Creates a new chain ID.
43    pub const fn new(id: u8) -> Self {
44        Self(id)
45    }
46
47    /// Returns the chain ID for mainnet (1).
48    pub const fn mainnet() -> Self {
49        Self(1)
50    }
51
52    /// Returns the chain ID for testnet (2).
53    pub const fn testnet() -> Self {
54        Self(2)
55    }
56
57    /// Returns the numeric chain ID value.
58    pub const fn id(&self) -> u8 {
59        self.0
60    }
61
62    /// Returns true if this is the mainnet chain ID.
63    pub const fn is_mainnet(&self) -> bool {
64        self.0 == 1
65    }
66
67    /// Returns true if this is the testnet chain ID.
68    pub const fn is_testnet(&self) -> bool {
69        self.0 == 2
70    }
71}
72
73impl Default for ChainId {
74    fn default() -> Self {
75        Self::testnet()
76    }
77}
78
79impl fmt::Debug for ChainId {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        write!(f, "ChainId({})", self.0)
82    }
83}
84
85impl fmt::Display for ChainId {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        write!(f, "{}", self.0)
88    }
89}
90
91impl From<u8> for ChainId {
92    fn from(id: u8) -> Self {
93        Self(id)
94    }
95}
96
97impl From<ChainId> for u8 {
98    fn from(chain_id: ChainId) -> Self {
99        chain_id.0
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_known_chain_ids() {
109        assert_eq!(ChainId::mainnet().id(), 1);
110        assert_eq!(ChainId::testnet().id(), 2);
111    }
112
113    #[test]
114    fn test_is_mainnet_testnet() {
115        assert!(ChainId::mainnet().is_mainnet());
116        assert!(!ChainId::mainnet().is_testnet());
117        assert!(ChainId::testnet().is_testnet());
118        assert!(!ChainId::testnet().is_mainnet());
119    }
120
121    #[test]
122    fn test_custom_chain_id() {
123        let custom = ChainId::new(42);
124        assert_eq!(custom.id(), 42);
125        assert!(!custom.is_mainnet());
126        assert!(!custom.is_testnet());
127    }
128
129    #[test]
130    fn test_json_serialization() {
131        let chain_id = ChainId::mainnet();
132        let json = serde_json::to_string(&chain_id).unwrap();
133        assert_eq!(json, "1");
134
135        let parsed: ChainId = serde_json::from_str(&json).unwrap();
136        assert_eq!(parsed, chain_id);
137    }
138
139    #[test]
140    fn test_default() {
141        let default = ChainId::default();
142        assert_eq!(default, ChainId::testnet());
143    }
144
145    #[test]
146    fn test_debug() {
147        let chain_id = ChainId::mainnet();
148        let debug = format!("{chain_id:?}");
149        assert_eq!(debug, "ChainId(1)");
150    }
151
152    #[test]
153    fn test_display() {
154        let chain_id = ChainId::testnet();
155        let display = format!("{chain_id}");
156        assert_eq!(display, "2");
157    }
158
159    #[test]
160    fn test_from_u8() {
161        let chain_id: ChainId = 3u8.into();
162        assert_eq!(chain_id.id(), 3);
163    }
164
165    #[test]
166    fn test_into_u8() {
167        let chain_id = ChainId::new(5);
168        let id: u8 = chain_id.into();
169        assert_eq!(id, 5);
170    }
171
172    #[test]
173    fn test_equality() {
174        assert_eq!(ChainId::new(1), ChainId::mainnet());
175        assert_ne!(ChainId::mainnet(), ChainId::testnet());
176    }
177
178    #[test]
179    fn test_hash() {
180        use std::collections::HashSet;
181        let mut set = HashSet::new();
182        set.insert(ChainId::mainnet());
183        set.insert(ChainId::testnet());
184        assert_eq!(set.len(), 2);
185        assert!(set.contains(&ChainId::new(1)));
186    }
187}