Skip to main content

libid_contracts/
factory.rs

1//! Bootstrap and use of the deterministic deployment factory.
2//!
3//! The [`LibidFactory`] proxy lives at the same address on every EVM network
4//! because every byte that feeds its address is frozen: it is deployed
5//! through the canonical keyless CREATE2 deployer (Arachnid's
6//! deterministic-deployment proxy at [`CREATE2_DEPLOYER`]) with fixed salts
7//! and init codes that carry no per-network data — the admin is the baked
8//! [`FACTORY_GENESIS_ADMIN`] constant, so initialization happens atomically
9//! inside the deployment. Protocol proxies are then deployed *through* the
10//! factory via CREATE3, which makes their addresses a function of
11//! `(factory, name)` only — see `solidity/contracts/factory/README.md`.
12//!
13//! [`ensure_factory`] is the whole bootstrap: check → install the CREATE2
14//! deployer if missing (via its well-known presigned transaction) → deploy
15//! the factory impl and proxy at their canonical addresses. There is
16//! deliberately no fallback deployment path: anything else would change the
17//! factory address and defeat the cross-network guarantee, so a chain that
18//! cannot take the presigned install transaction is a hard error.
19
20use 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
48/// Arachnid's deterministic-deployment proxy — deployed from a keyless
49/// one-time account, so it has this address on every chain that has it.
50/// Calldata format: 32-byte salt ++ init code.
51pub const CREATE2_DEPLOYER: Address =
52    address!("4e59b44847b379578588920cA78FbF26c0B4956C");
53
54/// The keyless one-time account the presigned install transaction spends
55/// from. It must hold the exact transaction cost (see
56/// [`CREATE2_DEPLOYER_FUNDING_WEI`]) before the broadcast.
57pub const CREATE2_DEPLOYER_SIGNER: Address =
58    address!("3fab184622dc19b6109349b94811493bf2a45362");
59
60/// What the install transaction costs: 100 gwei gas price × 100 000 gas
61/// limit = 0.01 ETH. The keyless account can never refund the surplus, so
62/// fund it with exactly this.
63pub const CREATE2_DEPLOYER_FUNDING_WEI: u128 = 10_000_000_000_000_000;
64
65/// The canonical presigned transaction that installs the CREATE2 deployer.
66///
67/// This is a pre-EIP-155 (no chain id, v = 27) legacy transaction whose
68/// signature was fixed *before any key existed* — r = s =
69/// 0x2222…22 — so nobody holds the sending key and the deployer lands at
70/// [`CREATE2_DEPLOYER`] on every chain that accepts it. Chains that enforce
71/// EIP-155 replay protection on all transactions reject it; per policy that
72/// is a hard error (see [`ensure_create2_deployer`]), not a cue for an
73/// alternate deployment path.
74pub const CREATE2_DEPLOYER_INSTALL_TX: &str = "0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222";
75
76/// The genesis admin baked into the frozen factory-proxy init code.
77/// PLACEHOLDER until the owner substitutes the protocol-admin KMS address.
78/// Keep in sync with `solidity/contracts/factory/FactoryGenesis.sol`.
79pub const FACTORY_GENESIS_ADMIN: Address =
80    address!("5bb76B0f81F028de363150602cC6d0Ca929E3C31");
81
82/// The 16-byte CREATE3 proxy init code (`Create3.PROXY_INITCODE`). Constant
83/// forever — its hash feeds every predicted address.
84pub 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
89/// Fixed salt of the factory implementation (`FactoryDeployer.IMPL_SALT`).
90pub fn factory_impl_salt() -> B256 {
91    keccak256("libid.factory.impl.v1")
92}
93
94/// Fixed salt of the factory proxy (`FactoryDeployer.PROXY_SALT`).
95pub fn factory_proxy_salt() -> B256 {
96    keccak256("libid.factory.v1")
97}
98
99/// The frozen implementation init code: LibidFactory's creation code, no
100/// constructor args.
101pub fn factory_impl_init_code(artifacts: &Artifacts) -> Result<Bytes> {
102    artifacts.bytecode("LibidFactory")
103}
104
105/// Where the factory implementation lands.
106pub 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
111/// The frozen proxy init code: ERC1967Proxy creation code ++
112/// abi.encode(implAddress, initialize(FACTORY_GENESIS_ADMIN)). Every byte is
113/// network-invariant; `FactoryDeployer.proxyInitCode()` produces the same
114/// bytes (asserted against the vendored artifacts by the Solidity tests).
115pub 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
126/// The canonical factory address — the same on every EVM network.
127pub 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
132/// The CREATE3 address `factory.deploy(name, ·)` lands on: the factory
133/// CREATE2-deploys the constant 16-byte proxy under `keccak256(name)`, and
134/// that proxy CREATE-deploys the target at its nonce 1. A pure function of
135/// `(factory, name)` — computable offline, before anything is deployed.
136pub 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
142/// Make sure the canonical CREATE2 deployer exists, installing it via the
143/// keyless presigned transaction if absent: fund the one-time signer with
144/// the exact transaction cost, then broadcast [`CREATE2_DEPLOYER_INSTALL_TX`].
145///
146/// Hard-errors on a chain that rejects the pre-EIP-155 transaction: there is
147/// no alternate deployment path (one would change the factory address), so
148/// such a network cannot host the deterministic factory.
149pub 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    // Fund the keyless one-time account up to the exact transaction cost.
161    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
218/// Deploy `salt ++ init_code` through the CREATE2 deployer and verify code
219/// landed at `predicted`.
220async 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
255/// Make sure the canonical factory exists at [`predict_factory_address`],
256/// bootstrapping whatever is missing: the CREATE2 deployer (via the keyless
257/// presigned transaction), the factory implementation, and the factory
258/// proxy — each at its deterministic address. Idempotent: reruns are
259/// read-only no-ops once the factory is up.
260pub 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
306/// Deploy `creation_code` under `name` through the factory (the provider's
307/// wallet must be the factory owner). Returns the deployed address, which
308/// always equals [`predict_address`]`(factory, name)`.
309pub async fn factory_deploy<P: Provider>(
310    provider: &P,
311    factory: Address,
312    name: &str,
313    creation_code: Bytes,
314) -> Result<Address> {
315    // `deploy` is built as raw calldata: alloy's `sol!` reserves the `deploy`
316    // method name on generated contract instances, so the typed call struct
317    // is used directly instead.
318    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}