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    deploy::deploy_via_create2,
43    error::{
44        Error,
45        Result,
46    },
47};
48
49/// Arachnid's deterministic-deployment proxy — deployed from a keyless
50/// one-time account, so it has this address on every chain that has it.
51/// Calldata format: 32-byte salt ++ init code.
52pub const CREATE2_DEPLOYER: Address =
53    address!("4e59b44847b379578588920cA78FbF26c0B4956C");
54
55/// The keyless one-time account the presigned install transaction spends
56/// from. It must hold the exact transaction cost (see
57/// [`CREATE2_DEPLOYER_FUNDING_WEI`]) before the broadcast.
58pub const CREATE2_DEPLOYER_SIGNER: Address =
59    address!("3fab184622dc19b6109349b94811493bf2a45362");
60
61/// What the install transaction costs: 100 gwei gas price × 100 000 gas
62/// limit = 0.01 ETH. The keyless account can never refund the surplus, so
63/// fund it with exactly this.
64pub const CREATE2_DEPLOYER_FUNDING_WEI: u128 = 10_000_000_000_000_000;
65
66/// The canonical presigned transaction that installs the CREATE2 deployer.
67///
68/// This is a pre-EIP-155 (no chain id, v = 27) legacy transaction whose
69/// signature was fixed *before any key existed* — r = s =
70/// 0x2222…22 — so nobody holds the sending key and the deployer lands at
71/// [`CREATE2_DEPLOYER`] on every chain that accepts it. Chains that enforce
72/// EIP-155 replay protection on all transactions reject it; per policy that
73/// is a hard error (see [`ensure_create2_deployer`]), not a cue for an
74/// alternate deployment path.
75pub const CREATE2_DEPLOYER_INSTALL_TX: &str = "0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222";
76
77/// The genesis admin baked into the frozen factory-proxy init code.
78/// PLACEHOLDER until the owner substitutes the protocol-admin KMS address.
79/// Keep in sync with `solidity/contracts/factory/FactoryGenesis.sol`.
80pub const FACTORY_GENESIS_ADMIN: Address =
81    address!("5bb76B0f81F028de363150602cC6d0Ca929E3C31");
82
83/// The 16-byte CREATE3 proxy init code (`Create3.PROXY_INITCODE`). Constant
84/// forever — its hash feeds every predicted address.
85pub const CREATE3_PROXY_INITCODE: [u8; 16] = [
86    0x67, 0x36, 0x3d, 0x3d, 0x37, 0x36, 0x3d, 0x34, 0xf0, 0x3d, 0x52, 0x60, 0x08, 0x60,
87    0x18, 0xf3,
88];
89
90/// Fixed salt of the factory implementation (`FactoryDeployer.IMPL_SALT`).
91pub fn factory_impl_salt() -> B256 {
92    keccak256("libid.factory.impl.v1")
93}
94
95/// Fixed salt of the factory proxy (`FactoryDeployer.PROXY_SALT`).
96pub fn factory_proxy_salt() -> B256 {
97    keccak256("libid.factory.v1")
98}
99
100/// The frozen implementation init code: LibidFactory's creation code, no
101/// constructor args.
102pub fn factory_impl_init_code(artifacts: &Artifacts) -> Result<Bytes> {
103    artifacts.bytecode("LibidFactory")
104}
105
106/// Where the factory implementation lands.
107pub fn predict_factory_impl_address(artifacts: &Artifacts) -> Result<Address> {
108    let init_code = factory_impl_init_code(artifacts)?;
109    Ok(CREATE2_DEPLOYER.create2(factory_impl_salt(), keccak256(&init_code)))
110}
111
112/// The frozen proxy init code: ERC1967Proxy creation code ++
113/// abi.encode(implAddress, initialize(FACTORY_GENESIS_ADMIN)). Every byte is
114/// network-invariant; `FactoryDeployer.proxyInitCode()` produces the same
115/// bytes (asserted against the vendored artifacts by the Solidity tests).
116pub fn factory_proxy_init_code(artifacts: &Artifacts) -> Result<Bytes> {
117    let impl_addr = predict_factory_impl_address(artifacts)?;
118    let init_data = LibidFactory::initializeCall {
119        owner_: FACTORY_GENESIS_ADMIN,
120    }
121    .abi_encode();
122    let mut code = artifacts.bytecode("ERC1967Proxy")?.to_vec();
123    code.extend_from_slice(&(impl_addr, Bytes::from(init_data)).abi_encode_params());
124    Ok(code.into())
125}
126
127/// The canonical factory address — the same on every EVM network.
128pub fn predict_factory_address(artifacts: &Artifacts) -> Result<Address> {
129    let init_code = factory_proxy_init_code(artifacts)?;
130    Ok(CREATE2_DEPLOYER.create2(factory_proxy_salt(), keccak256(&init_code)))
131}
132
133/// The CREATE3 address `factory.deploy(name, ·)` lands on: the factory
134/// CREATE2-deploys the constant 16-byte proxy under `keccak256(name)`, and
135/// that proxy CREATE-deploys the target at its nonce 1. A pure function of
136/// `(factory, name)` — computable offline, before anything is deployed.
137pub fn predict_address(factory: Address, name: &str) -> Address {
138    let salt = keccak256(name.as_bytes());
139    let create3_proxy = factory.create2(salt, keccak256(CREATE3_PROXY_INITCODE));
140    create3_proxy.create(1)
141}
142
143/// Make sure the canonical CREATE2 deployer exists, installing it via the
144/// keyless presigned transaction if absent: fund the one-time signer with
145/// the exact transaction cost, then broadcast [`CREATE2_DEPLOYER_INSTALL_TX`].
146///
147/// Hard-errors on a chain that rejects the pre-EIP-155 transaction: there is
148/// no alternate deployment path (one would change the factory address), so
149/// such a network cannot host the deterministic factory.
150pub async fn ensure_create2_deployer<P: Provider>(provider: &P) -> Result<()> {
151    let code = provider
152        .get_code_at(CREATE2_DEPLOYER)
153        .await
154        .map_err(|e| Error::Rpc {
155            detail: format!("failed to read code at the CREATE2 deployer: {e}"),
156        })?;
157    if !code.is_empty() {
158        return Ok(());
159    }
160
161    // Fund the keyless one-time account up to the exact transaction cost.
162    let balance = provider
163        .get_balance(CREATE2_DEPLOYER_SIGNER)
164        .await
165        .map_err(|e| Error::Rpc {
166            detail: format!("failed to read the CREATE2 deployer signer balance: {e}"),
167        })?;
168    let needed = U256::from(CREATE2_DEPLOYER_FUNDING_WEI);
169    if balance < needed {
170        let tx = TransactionRequest::default()
171            .with_to(CREATE2_DEPLOYER_SIGNER)
172            .with_value(needed - balance);
173        let pending = provider
174            .send_transaction(tx)
175            .await
176            .map_err(|e| Error::Rpc {
177                detail: format!("failed to fund the CREATE2 deployer signer: {e}"),
178            })?;
179        pending.get_receipt().await.map_err(|e| Error::Rpc {
180            detail: format!("CREATE2 deployer funding confirmation failed: {e}"),
181        })?;
182    }
183
184    let raw = hex::decode(CREATE2_DEPLOYER_INSTALL_TX).map_err(|e| Error::Rpc {
185        detail: format!("bad CREATE2 deployer install tx constant: {e}"),
186    })?;
187    let pending = provider
188        .send_raw_transaction(&raw)
189        .await
190        .map_err(|e| Error::Rpc {
191            detail: format!(
192                "this chain rejected the keyless (pre-EIP-155) install transaction \
193                 for the canonical CREATE2 deployer: {e}. There is deliberately no \
194                 fallback deployment path — any other route would change the \
195                 factory address and defeat the cross-network guarantee — so this \
196                 network cannot host the deterministic factory and must be \
197                 reconsidered."
198            ),
199        })?;
200    pending.get_receipt().await.map_err(|e| Error::Rpc {
201        detail: format!("CREATE2 deployer install confirmation failed: {e}"),
202    })?;
203
204    let code = provider
205        .get_code_at(CREATE2_DEPLOYER)
206        .await
207        .map_err(|e| Error::Rpc {
208            detail: format!("failed to re-read code at the CREATE2 deployer: {e}"),
209        })?;
210    if code.is_empty() {
211        return Err(Error::Rpc {
212            detail: "the CREATE2 deployer install transaction landed but left no code"
213                .into(),
214        });
215    }
216    Ok(())
217}
218
219/// Make sure the canonical factory exists at [`predict_factory_address`],
220/// bootstrapping whatever is missing: the CREATE2 deployer (via the keyless
221/// presigned transaction), the factory implementation, and the factory
222/// proxy — each at its deterministic address. Idempotent: reruns are
223/// read-only no-ops once the factory is up.
224pub async fn ensure_factory<P: Provider>(
225    provider: &P,
226    artifacts: &Artifacts,
227) -> Result<Address> {
228    let factory = predict_factory_address(artifacts)?;
229    let code = provider
230        .get_code_at(factory)
231        .await
232        .map_err(|e| Error::Rpc {
233            detail: format!("failed to read code at the factory address: {e}"),
234        })?;
235    if !code.is_empty() {
236        return Ok(factory);
237    }
238
239    ensure_create2_deployer(provider).await?;
240
241    let impl_addr = predict_factory_impl_address(artifacts)?;
242    let impl_code = provider
243        .get_code_at(impl_addr)
244        .await
245        .map_err(|e| Error::Rpc {
246            detail: format!("failed to read code at the factory impl address: {e}"),
247        })?;
248    if impl_code.is_empty() {
249        deploy_via_create2(
250            provider,
251            factory_impl_salt(),
252            &factory_impl_init_code(artifacts)?,
253            impl_addr,
254            "LibidFactory (impl)",
255            None,
256        )
257        .await?;
258    }
259
260    deploy_via_create2(
261        provider,
262        factory_proxy_salt(),
263        &factory_proxy_init_code(artifacts)?,
264        factory,
265        "LibidFactory (proxy)",
266        None,
267    )
268    .await?;
269    Ok(factory)
270}
271
272/// Deploy `creation_code` under `name` through the factory (the provider's
273/// wallet must be the factory owner). Returns the deployed address, which
274/// always equals [`predict_address`]`(factory, name)`.
275pub async fn factory_deploy<P: Provider>(
276    provider: &P,
277    factory: Address,
278    name: &str,
279    creation_code: Bytes,
280) -> Result<Address> {
281    // `deploy` is built as raw calldata: alloy's `sol!` reserves the `deploy`
282    // method name on generated contract instances, so the typed call struct
283    // is used directly instead.
284    let call = LibidFactory::deployCall {
285        name: name.to_string(),
286        creationCode: creation_code,
287    };
288    let tx = TransactionRequest::default()
289        .with_to(factory)
290        .with_input(Bytes::from(call.abi_encode()));
291    let pending = provider
292        .send_transaction(tx)
293        .await
294        .map_err(|e| Error::Rpc {
295            detail: format!("factory deploy of {name} send failed: {e}"),
296        })?;
297    pending.get_receipt().await.map_err(|e| Error::Rpc {
298        detail: format!("factory deploy of {name} confirmation failed: {e}"),
299    })?;
300
301    let contract = LibidFactory::new(factory, provider);
302    let addr = contract
303        .deployedAt(name.to_string())
304        .call()
305        .await
306        .map_err(|e| Error::Rpc {
307            detail: format!("factory deployedAt({name}) read failed: {e}"),
308        })?;
309    if addr == Address::ZERO {
310        return Err(Error::Rpc {
311            detail: format!("factory deploy of {name} landed no recorded address"),
312        });
313    }
314    Ok(addr)
315}