safe_rs/chain/
config.rs

1//! Chain configuration for Safe contract addresses
2
3use alloy::primitives::{address, Address};
4
5/// Canonical Safe v1.4.1 contract addresses
6/// These addresses are the same across all supported chains (CREATE2 deployment)
7#[derive(Debug, Clone)]
8pub struct ChainAddresses {
9    /// Safe singleton address
10    pub safe_singleton: Address,
11    /// MultiSend contract address
12    pub multi_send: Address,
13    /// MultiSendCallOnly contract address
14    pub multi_send_call_only: Address,
15    /// Safe proxy factory address
16    pub proxy_factory: Address,
17    /// Compatibility fallback handler
18    pub fallback_handler: Address,
19}
20
21impl Default for ChainAddresses {
22    fn default() -> Self {
23        Self::v1_4_1()
24    }
25}
26
27impl ChainAddresses {
28    /// Returns the canonical Safe v1.4.1 addresses
29    pub fn v1_4_1() -> Self {
30        Self {
31            safe_singleton: address!("41675C099F32341bf84BFc5382aF534df5C7461a"),
32            multi_send: address!("38869bf66a61cF6bDB996A6aE40D5853Fd43B526"),
33            multi_send_call_only: address!("9641d764fc13c8B624c04430C7356C1C7C8102e2"),
34            proxy_factory: address!("4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67"),
35            fallback_handler: address!("fd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99"),
36        }
37    }
38
39    /// Returns the canonical Safe v1.3.0 addresses (for backwards compatibility)
40    pub fn v1_3_0() -> Self {
41        Self {
42            safe_singleton: address!("d9Db270c1B5E3Bd161E8c8503c55cEABeE709552"),
43            multi_send: address!("A238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761"),
44            multi_send_call_only: address!("40A2aCCbd92BCA938b02010E17A5b8929b49130D"),
45            proxy_factory: address!("a6B71E26C5e0845f74c812102Ca7114b6a896AB2"),
46            fallback_handler: address!("f48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4"),
47        }
48    }
49
50    /// Creates a custom address configuration
51    pub fn custom(
52        safe_singleton: Address,
53        multi_send: Address,
54        multi_send_call_only: Address,
55    ) -> Self {
56        Self {
57            safe_singleton,
58            multi_send,
59            multi_send_call_only,
60            proxy_factory: Address::ZERO,
61            fallback_handler: Address::ZERO,
62        }
63    }
64}
65
66/// Chain configuration including addresses and chain ID
67#[derive(Debug, Clone)]
68pub struct ChainConfig {
69    /// Chain ID
70    pub chain_id: u64,
71    /// Contract addresses
72    pub addresses: ChainAddresses,
73}
74
75impl ChainConfig {
76    /// Creates a new chain configuration with canonical v1.4.1 addresses
77    pub fn new(chain_id: u64) -> Self {
78        Self {
79            chain_id,
80            addresses: ChainAddresses::v1_4_1(),
81        }
82    }
83
84    /// Creates a chain configuration with custom addresses
85    pub fn with_addresses(chain_id: u64, addresses: ChainAddresses) -> Self {
86        Self { chain_id, addresses }
87    }
88
89    /// Returns configuration for Ethereum mainnet
90    pub fn mainnet() -> Self {
91        Self::new(1)
92    }
93
94    /// Returns configuration for Sepolia testnet
95    pub fn sepolia() -> Self {
96        Self::new(11155111)
97    }
98
99    /// Returns configuration for Arbitrum
100    pub fn arbitrum() -> Self {
101        Self::new(42161)
102    }
103
104    /// Returns configuration for Optimism
105    pub fn optimism() -> Self {
106        Self::new(10)
107    }
108
109    /// Returns configuration for Base
110    pub fn base() -> Self {
111        Self::new(8453)
112    }
113
114    /// Returns configuration for Polygon
115    pub fn polygon() -> Self {
116        Self::new(137)
117    }
118}
119
120/// Well-known chain IDs
121pub mod chain_ids {
122    pub const MAINNET: u64 = 1;
123    pub const SEPOLIA: u64 = 11155111;
124    pub const ARBITRUM: u64 = 42161;
125    pub const OPTIMISM: u64 = 10;
126    pub const BASE: u64 = 8453;
127    pub const POLYGON: u64 = 137;
128    pub const BSC: u64 = 56;
129    pub const AVALANCHE: u64 = 43114;
130    pub const GNOSIS: u64 = 100;
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn test_v1_4_1_addresses() {
139        let addrs = ChainAddresses::v1_4_1();
140        assert_eq!(
141            addrs.safe_singleton,
142            address!("41675C099F32341bf84BFc5382aF534df5C7461a")
143        );
144        assert_eq!(
145            addrs.multi_send,
146            address!("38869bf66a61cF6bDB996A6aE40D5853Fd43B526")
147        );
148        assert_eq!(
149            addrs.multi_send_call_only,
150            address!("9641d764fc13c8B624c04430C7356C1C7C8102e2")
151        );
152    }
153
154    #[test]
155    fn test_chain_config_mainnet() {
156        let config = ChainConfig::mainnet();
157        assert_eq!(config.chain_id, 1);
158    }
159
160    #[test]
161    fn test_default_addresses() {
162        let default = ChainAddresses::default();
163        let v1_4_1 = ChainAddresses::v1_4_1();
164        assert_eq!(default.safe_singleton, v1_4_1.safe_singleton);
165    }
166}