libid_contracts/
factory.rs1use alloy::{
21 hex,
22 network::TransactionBuilder,
23 primitives::{
24 address,
25 keccak256,
26 Address,
27 Bytes,
28 B256,
29 U256,
30 },
31 providers::Provider,
32 rpc::types::TransactionRequest,
33 sol_types::{
34 SolCall,
35 SolValue,
36 },
37};
38
39use crate::{
40 artifacts::Artifacts,
41 bindings::factory::LibidFactory,
42 error::{
43 Error,
44 Result,
45 },
46};
47
48pub const CREATE2_DEPLOYER: Address =
52 address!("4e59b44847b379578588920cA78FbF26c0B4956C");
53
54pub const CREATE2_DEPLOYER_SIGNER: Address =
58 address!("3fab184622dc19b6109349b94811493bf2a45362");
59
60pub const CREATE2_DEPLOYER_FUNDING_WEI: u128 = 10_000_000_000_000_000;
64
65pub const CREATE2_DEPLOYER_INSTALL_TX: &str = "0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222";
75
76pub const FACTORY_GENESIS_ADMIN: Address =
80 address!("5bb76B0f81F028de363150602cC6d0Ca929E3C31");
81
82pub const CREATE3_PROXY_INITCODE: [u8; 16] = [
85 0x67, 0x36, 0x3d, 0x3d, 0x37, 0x36, 0x3d, 0x34, 0xf0, 0x3d, 0x52, 0x60, 0x08, 0x60,
86 0x18, 0xf3,
87];
88
89pub fn factory_impl_salt() -> B256 {
91 keccak256("libid.factory.impl.v1")
92}
93
94pub fn factory_proxy_salt() -> B256 {
96 keccak256("libid.factory.v1")
97}
98
99pub fn factory_impl_init_code(artifacts: &Artifacts) -> Result<Bytes> {
102 artifacts.bytecode("LibidFactory")
103}
104
105pub fn predict_factory_impl_address(artifacts: &Artifacts) -> Result<Address> {
107 let init_code = factory_impl_init_code(artifacts)?;
108 Ok(CREATE2_DEPLOYER.create2(factory_impl_salt(), keccak256(&init_code)))
109}
110
111pub fn factory_proxy_init_code(artifacts: &Artifacts) -> Result<Bytes> {
116 let impl_addr = predict_factory_impl_address(artifacts)?;
117 let init_data = LibidFactory::initializeCall {
118 owner_: FACTORY_GENESIS_ADMIN,
119 }
120 .abi_encode();
121 let mut code = artifacts.bytecode("ERC1967Proxy")?.to_vec();
122 code.extend_from_slice(&(impl_addr, Bytes::from(init_data)).abi_encode_params());
123 Ok(code.into())
124}
125
126pub fn predict_factory_address(artifacts: &Artifacts) -> Result<Address> {
128 let init_code = factory_proxy_init_code(artifacts)?;
129 Ok(CREATE2_DEPLOYER.create2(factory_proxy_salt(), keccak256(&init_code)))
130}
131
132pub fn predict_address(factory: Address, name: &str) -> Address {
137 let salt = keccak256(name.as_bytes());
138 let create3_proxy = factory.create2(salt, keccak256(CREATE3_PROXY_INITCODE));
139 create3_proxy.create(1)
140}
141
142pub async fn ensure_create2_deployer<P: Provider>(provider: &P) -> Result<()> {
150 let code = provider
151 .get_code_at(CREATE2_DEPLOYER)
152 .await
153 .map_err(|e| Error::Rpc {
154 detail: format!("failed to read code at the CREATE2 deployer: {e}"),
155 })?;
156 if !code.is_empty() {
157 return Ok(());
158 }
159
160 let balance = provider
162 .get_balance(CREATE2_DEPLOYER_SIGNER)
163 .await
164 .map_err(|e| Error::Rpc {
165 detail: format!("failed to read the CREATE2 deployer signer balance: {e}"),
166 })?;
167 let needed = U256::from(CREATE2_DEPLOYER_FUNDING_WEI);
168 if balance < needed {
169 let tx = TransactionRequest::default()
170 .with_to(CREATE2_DEPLOYER_SIGNER)
171 .with_value(needed - balance);
172 let pending = provider
173 .send_transaction(tx)
174 .await
175 .map_err(|e| Error::Rpc {
176 detail: format!("failed to fund the CREATE2 deployer signer: {e}"),
177 })?;
178 pending.get_receipt().await.map_err(|e| Error::Rpc {
179 detail: format!("CREATE2 deployer funding confirmation failed: {e}"),
180 })?;
181 }
182
183 let raw = hex::decode(CREATE2_DEPLOYER_INSTALL_TX).map_err(|e| Error::Rpc {
184 detail: format!("bad CREATE2 deployer install tx constant: {e}"),
185 })?;
186 let pending = provider
187 .send_raw_transaction(&raw)
188 .await
189 .map_err(|e| Error::Rpc {
190 detail: format!(
191 "this chain rejected the keyless (pre-EIP-155) install transaction \
192 for the canonical CREATE2 deployer: {e}. There is deliberately no \
193 fallback deployment path — any other route would change the \
194 factory address and defeat the cross-network guarantee — so this \
195 network cannot host the deterministic factory and must be \
196 reconsidered."
197 ),
198 })?;
199 pending.get_receipt().await.map_err(|e| Error::Rpc {
200 detail: format!("CREATE2 deployer install confirmation failed: {e}"),
201 })?;
202
203 let code = provider
204 .get_code_at(CREATE2_DEPLOYER)
205 .await
206 .map_err(|e| Error::Rpc {
207 detail: format!("failed to re-read code at the CREATE2 deployer: {e}"),
208 })?;
209 if code.is_empty() {
210 return Err(Error::Rpc {
211 detail: "the CREATE2 deployer install transaction landed but left no code"
212 .into(),
213 });
214 }
215 Ok(())
216}
217
218async fn deploy_via_create2<P: Provider>(
221 provider: &P,
222 salt: B256,
223 init_code: &[u8],
224 predicted: Address,
225 label: &str,
226) -> Result<()> {
227 let mut input = salt.to_vec();
228 input.extend_from_slice(init_code);
229 let tx = TransactionRequest::default()
230 .with_to(CREATE2_DEPLOYER)
231 .with_input(Bytes::from(input));
232 let pending = provider
233 .send_transaction(tx)
234 .await
235 .map_err(|e| Error::Rpc {
236 detail: format!("{label}: CREATE2 deploy send failed: {e}"),
237 })?;
238 pending.get_receipt().await.map_err(|e| Error::Rpc {
239 detail: format!("{label}: CREATE2 deploy confirmation failed: {e}"),
240 })?;
241 let code = provider
242 .get_code_at(predicted)
243 .await
244 .map_err(|e| Error::Rpc {
245 detail: format!("{label}: failed to read code at {predicted}: {e}"),
246 })?;
247 if code.is_empty() {
248 return Err(Error::Rpc {
249 detail: format!("{label}: no code at the predicted address {predicted}"),
250 });
251 }
252 Ok(())
253}
254
255pub async fn ensure_factory<P: Provider>(
261 provider: &P,
262 artifacts: &Artifacts,
263) -> Result<Address> {
264 let factory = predict_factory_address(artifacts)?;
265 let code = provider
266 .get_code_at(factory)
267 .await
268 .map_err(|e| Error::Rpc {
269 detail: format!("failed to read code at the factory address: {e}"),
270 })?;
271 if !code.is_empty() {
272 return Ok(factory);
273 }
274
275 ensure_create2_deployer(provider).await?;
276
277 let impl_addr = predict_factory_impl_address(artifacts)?;
278 let impl_code = provider
279 .get_code_at(impl_addr)
280 .await
281 .map_err(|e| Error::Rpc {
282 detail: format!("failed to read code at the factory impl address: {e}"),
283 })?;
284 if impl_code.is_empty() {
285 deploy_via_create2(
286 provider,
287 factory_impl_salt(),
288 &factory_impl_init_code(artifacts)?,
289 impl_addr,
290 "LibidFactory (impl)",
291 )
292 .await?;
293 }
294
295 deploy_via_create2(
296 provider,
297 factory_proxy_salt(),
298 &factory_proxy_init_code(artifacts)?,
299 factory,
300 "LibidFactory (proxy)",
301 )
302 .await?;
303 Ok(factory)
304}
305
306pub async fn factory_deploy<P: Provider>(
310 provider: &P,
311 factory: Address,
312 name: &str,
313 creation_code: Bytes,
314) -> Result<Address> {
315 let call = LibidFactory::deployCall {
319 name: name.to_string(),
320 creationCode: creation_code,
321 };
322 let tx = TransactionRequest::default()
323 .with_to(factory)
324 .with_input(Bytes::from(call.abi_encode()));
325 let pending = provider
326 .send_transaction(tx)
327 .await
328 .map_err(|e| Error::Rpc {
329 detail: format!("factory deploy of {name} send failed: {e}"),
330 })?;
331 pending.get_receipt().await.map_err(|e| Error::Rpc {
332 detail: format!("factory deploy of {name} confirmation failed: {e}"),
333 })?;
334
335 let contract = LibidFactory::new(factory, provider);
336 let addr = contract
337 .deployedAt(name.to_string())
338 .call()
339 .await
340 .map_err(|e| Error::Rpc {
341 detail: format!("factory deployedAt({name}) read failed: {e}"),
342 })?;
343 if addr == Address::ZERO {
344 return Err(Error::Rpc {
345 detail: format!("factory deploy of {name} landed no recorded address"),
346 });
347 }
348 Ok(addr)
349}