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 deploy::deploy_via_create2,
43 error::{
44 Error,
45 Result,
46 },
47};
48
49pub const CREATE2_DEPLOYER: Address =
53 address!("4e59b44847b379578588920cA78FbF26c0B4956C");
54
55pub const CREATE2_DEPLOYER_SIGNER: Address =
59 address!("3fab184622dc19b6109349b94811493bf2a45362");
60
61pub const CREATE2_DEPLOYER_FUNDING_WEI: u128 = 10_000_000_000_000_000;
65
66pub const CREATE2_DEPLOYER_INSTALL_TX: &str = "0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222";
76
77pub const CREATE3_PROXY_INITCODE: [u8; 16] = [
80 0x67, 0x36, 0x3d, 0x3d, 0x37, 0x36, 0x3d, 0x34, 0xf0, 0x3d, 0x52, 0x60, 0x08, 0x60,
81 0x18, 0xf3,
82];
83
84pub fn factory_impl_salt() -> B256 {
86 keccak256("libid.factory.impl.v1")
87}
88
89pub fn factory_proxy_salt() -> B256 {
91 keccak256("libid.factory.v1")
92}
93
94pub fn factory_impl_init_code(artifacts: &Artifacts) -> Result<Bytes> {
97 artifacts.bytecode("LibidFactory")
98}
99
100pub fn predict_factory_impl_address(artifacts: &Artifacts) -> Result<Address> {
102 let init_code = factory_impl_init_code(artifacts)?;
103 Ok(CREATE2_DEPLOYER.create2(factory_impl_salt(), keccak256(&init_code)))
104}
105
106#[derive(Clone, Copy, Debug, PartialEq, Eq)]
112pub struct FactoryGenesis {
113 pub admin: Address,
115}
116
117impl FactoryGenesis {
118 pub fn proxy_init_code(&self, artifacts: &Artifacts) -> Result<Bytes> {
123 let impl_addr = predict_factory_impl_address(artifacts)?;
124 let init_data = LibidFactory::initializeCall { owner_: self.admin }.abi_encode();
125 let mut code = artifacts.bytecode("ERC1967Proxy")?.to_vec();
126 code.extend_from_slice(&(impl_addr, Bytes::from(init_data)).abi_encode_params());
127 Ok(code.into())
128 }
129
130 pub fn address(&self, artifacts: &Artifacts) -> Result<Address> {
133 let init_code = self.proxy_init_code(artifacts)?;
134 Ok(CREATE2_DEPLOYER.create2(factory_proxy_salt(), keccak256(&init_code)))
135 }
136
137 pub async fn ensure<P: Provider>(
143 &self,
144 provider: &P,
145 artifacts: &Artifacts,
146 ) -> Result<Address> {
147 let factory = self.address(artifacts)?;
148 let code = provider
149 .get_code_at(factory)
150 .await
151 .map_err(|e| Error::Rpc {
152 detail: format!("failed to read code at the factory address: {e}"),
153 })?;
154 if !code.is_empty() {
155 return Ok(factory);
156 }
157
158 ensure_create2_deployer(provider).await?;
159
160 let impl_addr = predict_factory_impl_address(artifacts)?;
161 let impl_code =
162 provider
163 .get_code_at(impl_addr)
164 .await
165 .map_err(|e| Error::Rpc {
166 detail: format!(
167 "failed to read code at the factory impl address: {e}"
168 ),
169 })?;
170 if impl_code.is_empty() {
171 deploy_via_create2(
172 provider,
173 factory_impl_salt(),
174 &factory_impl_init_code(artifacts)?,
175 impl_addr,
176 "LibidFactory (impl)",
177 None,
178 )
179 .await?;
180 }
181
182 deploy_via_create2(
183 provider,
184 factory_proxy_salt(),
185 &self.proxy_init_code(artifacts)?,
186 factory,
187 "LibidFactory (proxy)",
188 None,
189 )
190 .await?;
191 Ok(factory)
192 }
193}
194
195pub fn predict_address(factory: Address, name: &str) -> Address {
200 let salt = keccak256(name.as_bytes());
201 let create3_proxy = factory.create2(salt, keccak256(CREATE3_PROXY_INITCODE));
202 create3_proxy.create(1)
203}
204
205pub async fn ensure_create2_deployer<P: Provider>(provider: &P) -> Result<()> {
213 let code = provider
214 .get_code_at(CREATE2_DEPLOYER)
215 .await
216 .map_err(|e| Error::Rpc {
217 detail: format!("failed to read code at the CREATE2 deployer: {e}"),
218 })?;
219 if !code.is_empty() {
220 return Ok(());
221 }
222
223 let balance = provider
225 .get_balance(CREATE2_DEPLOYER_SIGNER)
226 .await
227 .map_err(|e| Error::Rpc {
228 detail: format!("failed to read the CREATE2 deployer signer balance: {e}"),
229 })?;
230 let needed = U256::from(CREATE2_DEPLOYER_FUNDING_WEI);
231 if balance < needed {
232 let tx = TransactionRequest::default()
233 .with_to(CREATE2_DEPLOYER_SIGNER)
234 .with_value(needed - balance);
235 let pending = provider
236 .send_transaction(tx)
237 .await
238 .map_err(|e| Error::Rpc {
239 detail: format!("failed to fund the CREATE2 deployer signer: {e}"),
240 })?;
241 pending.get_receipt().await.map_err(|e| Error::Rpc {
242 detail: format!("CREATE2 deployer funding confirmation failed: {e}"),
243 })?;
244 }
245
246 let raw = hex::decode(CREATE2_DEPLOYER_INSTALL_TX).map_err(|e| Error::Rpc {
247 detail: format!("bad CREATE2 deployer install tx constant: {e}"),
248 })?;
249 let pending = provider
250 .send_raw_transaction(&raw)
251 .await
252 .map_err(|e| Error::Rpc {
253 detail: format!(
254 "this chain rejected the keyless (pre-EIP-155) install transaction \
255 for the canonical CREATE2 deployer: {e}. There is deliberately no \
256 fallback deployment path — any other route would change the \
257 factory address and defeat the cross-network guarantee — so this \
258 network cannot host the deterministic factory and must be \
259 reconsidered."
260 ),
261 })?;
262 pending.get_receipt().await.map_err(|e| Error::Rpc {
263 detail: format!("CREATE2 deployer install confirmation failed: {e}"),
264 })?;
265
266 let code = provider
267 .get_code_at(CREATE2_DEPLOYER)
268 .await
269 .map_err(|e| Error::Rpc {
270 detail: format!("failed to re-read code at the CREATE2 deployer: {e}"),
271 })?;
272 if code.is_empty() {
273 return Err(Error::Rpc {
274 detail: "the CREATE2 deployer install transaction landed but left no code"
275 .into(),
276 });
277 }
278 Ok(())
279}
280
281pub async fn factory_deploy<P: Provider>(
285 provider: &P,
286 factory: Address,
287 name: &str,
288 creation_code: Bytes,
289) -> Result<Address> {
290 let call = LibidFactory::deployCall {
294 name: name.to_string(),
295 creationCode: creation_code,
296 };
297 let tx = TransactionRequest::default()
298 .with_to(factory)
299 .with_input(Bytes::from(call.abi_encode()));
300 let pending = provider
301 .send_transaction(tx)
302 .await
303 .map_err(|e| Error::Rpc {
304 detail: format!("factory deploy of {name} send failed: {e}"),
305 })?;
306 pending.get_receipt().await.map_err(|e| Error::Rpc {
307 detail: format!("factory deploy of {name} confirmation failed: {e}"),
308 })?;
309
310 let contract = LibidFactory::new(factory, provider);
311 let addr = contract
312 .deployedAt(name.to_string())
313 .call()
314 .await
315 .map_err(|e| Error::Rpc {
316 detail: format!("factory deployedAt({name}) read failed: {e}"),
317 })?;
318 if addr == Address::ZERO {
319 return Err(Error::Rpc {
320 detail: format!("factory deploy of {name} landed no recorded address"),
321 });
322 }
323 Ok(addr)
324}