Skip to main content

safe_rs/
create2.rs

1//! CREATE2 address computation for Safe proxy deployment
2//!
3//! This module provides utilities for computing deterministic Safe proxy addresses
4//! using CREATE2. The Safe proxy factory deploys proxies at deterministic addresses
5//! based on the singleton address, initializer data, and salt nonce.
6
7use alloy::primitives::{keccak256, Address, Bytes, U256};
8use alloy::sol_types::SolCall;
9
10use crate::contracts::ISafeSetup;
11
12/// Encodes the Safe.setup() call for proxy initialization
13///
14/// # Arguments
15/// * `owners` - Array of owner addresses for the Safe
16/// * `threshold` - Number of required confirmations for transactions
17/// * `fallback_handler` - Address of the fallback handler contract
18///
19/// # Returns
20/// ABI-encoded setup call data
21pub fn encode_setup_call(owners: &[Address], threshold: u64, fallback_handler: Address) -> Bytes {
22    let setup_call = ISafeSetup::setupCall {
23        _owners: owners.to_vec(),
24        _threshold: U256::from(threshold),
25        to: Address::ZERO,
26        data: Bytes::new(),
27        fallbackHandler: fallback_handler,
28        paymentToken: Address::ZERO,
29        payment: U256::ZERO,
30        paymentReceiver: Address::ZERO,
31    };
32
33    Bytes::from(setup_call.abi_encode())
34}
35
36/// Computes the CREATE2 address for a Safe proxy
37///
38/// The Safe proxy factory uses a specific CREATE2 formula:
39/// ```text
40/// salt = keccak256(keccak256(initializer) ++ saltNonce)
41/// init_code = proxyCreationCode ++ singleton_address_padded
42/// address = keccak256(0xff ++ factory ++ salt ++ keccak256(init_code))[12:]
43/// ```
44///
45/// # Arguments
46/// * `factory` - Address of the SafeProxyFactory contract
47/// * `singleton` - Address of the Safe singleton (implementation) contract
48/// * `initializer` - ABI-encoded Safe.setup() call data
49/// * `salt_nonce` - User-provided nonce for address derivation
50/// * `creation_code` - Proxy creation bytecode from SafeProxyFactory.proxyCreationCode()
51///
52/// # Returns
53/// The deterministic address where the Safe proxy will be deployed
54pub fn compute_create2_address(
55    factory: Address,
56    singleton: Address,
57    initializer: &Bytes,
58    salt_nonce: U256,
59    creation_code: &Bytes,
60) -> Address {
61    // Compute salt: keccak256(keccak256(initializer) ++ saltNonce)
62    let initializer_hash = keccak256(initializer);
63
64    let mut salt_input = [0u8; 64];
65    salt_input[..32].copy_from_slice(initializer_hash.as_slice());
66    salt_input[32..64].copy_from_slice(&salt_nonce.to_be_bytes::<32>());
67
68    let salt = keccak256(salt_input);
69
70    // Compute init_code_hash: keccak256(creation_code ++ singleton_padded)
71    let mut init_code = creation_code.to_vec();
72    // Append singleton address as 32-byte padded value
73    let mut singleton_padded = [0u8; 32];
74    singleton_padded[12..].copy_from_slice(singleton.as_slice());
75    init_code.extend_from_slice(&singleton_padded);
76
77    let init_code_hash = keccak256(&init_code);
78
79    // Compute CREATE2 address
80    let mut create2_input = Vec::with_capacity(1 + 20 + 32 + 32);
81    create2_input.push(0xff);
82    create2_input.extend_from_slice(factory.as_slice());
83    create2_input.extend_from_slice(salt.as_slice());
84    create2_input.extend_from_slice(init_code_hash.as_slice());
85
86    let hash = keccak256(&create2_input);
87
88    Address::from_slice(&hash[12..])
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use alloy::primitives::address;
95
96    #[test]
97    fn test_encode_setup_call() {
98        let owners = vec![address!("1234567890123456789012345678901234567890")];
99        let threshold = 1;
100        let fallback_handler = address!("fd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99");
101
102        let data = encode_setup_call(&owners, threshold, fallback_handler);
103
104        // Should not be empty and should start with setup selector
105        assert!(!data.is_empty());
106        // setup() selector is 0xb63e800d
107        assert_eq!(&data[0..4], &[0xb6, 0x3e, 0x80, 0x0d]);
108    }
109
110    #[test]
111    fn test_encode_setup_call_multiple_owners() {
112        let owners = vec![
113            address!("1111111111111111111111111111111111111111"),
114            address!("2222222222222222222222222222222222222222"),
115            address!("3333333333333333333333333333333333333333"),
116        ];
117        let threshold = 2;
118        let fallback_handler = address!("fd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99");
119
120        let data = encode_setup_call(&owners, threshold, fallback_handler);
121
122        // Should not be empty
123        assert!(!data.is_empty());
124        // setup() selector is 0xb63e800d
125        assert_eq!(&data[0..4], &[0xb6, 0x3e, 0x80, 0x0d]);
126    }
127
128    #[test]
129    fn test_compute_create2_address_deterministic() {
130        // Test that the same inputs always produce the same output
131        let factory = address!("4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67");
132        let singleton = address!("41675C099F32341bf84BFc5382aF534df5C7461a");
133        let initializer = Bytes::from(vec![0x01, 0x02, 0x03]);
134        let salt_nonce = U256::from(42);
135        let creation_code = Bytes::from(vec![0x60, 0x80, 0x60, 0x40]);
136
137        let addr1 = compute_create2_address(
138            factory,
139            singleton,
140            &initializer,
141            salt_nonce,
142            &creation_code,
143        );
144
145        let addr2 = compute_create2_address(
146            factory,
147            singleton,
148            &initializer,
149            salt_nonce,
150            &creation_code,
151        );
152
153        assert_eq!(addr1, addr2, "CREATE2 address should be deterministic");
154    }
155
156    #[test]
157    fn test_compute_create2_address_different_nonce() {
158        // Test that different salt nonces produce different addresses
159        let factory = address!("4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67");
160        let singleton = address!("41675C099F32341bf84BFc5382aF534df5C7461a");
161        let initializer = Bytes::from(vec![0x01, 0x02, 0x03]);
162        let creation_code = Bytes::from(vec![0x60, 0x80, 0x60, 0x40]);
163
164        let addr1 = compute_create2_address(
165            factory,
166            singleton,
167            &initializer,
168            U256::from(1),
169            &creation_code,
170        );
171
172        let addr2 = compute_create2_address(
173            factory,
174            singleton,
175            &initializer,
176            U256::from(2),
177            &creation_code,
178        );
179
180        assert_ne!(addr1, addr2, "Different salt nonces should produce different addresses");
181    }
182}