use alloy::{
hex,
network::TransactionBuilder,
primitives::{
address,
keccak256,
Address,
Bytes,
B256,
U256,
},
providers::Provider,
rpc::types::TransactionRequest,
sol_types::{
SolCall,
SolValue,
},
};
use crate::{
artifacts::Artifacts,
bindings::factory::LibidFactory,
deploy::deploy_via_create2,
error::{
Error,
Result,
},
};
pub const CREATE2_DEPLOYER: Address =
address!("4e59b44847b379578588920cA78FbF26c0B4956C");
pub const CREATE2_DEPLOYER_SIGNER: Address =
address!("3fab184622dc19b6109349b94811493bf2a45362");
pub const CREATE2_DEPLOYER_FUNDING_WEI: u128 = 10_000_000_000_000_000;
pub const CREATE2_DEPLOYER_INSTALL_TX: &str = "0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222";
pub const CREATE3_PROXY_INITCODE: [u8; 16] = [
0x67, 0x36, 0x3d, 0x3d, 0x37, 0x36, 0x3d, 0x34, 0xf0, 0x3d, 0x52, 0x60, 0x08, 0x60,
0x18, 0xf3,
];
pub fn factory_impl_salt() -> B256 {
keccak256("libid.factory.impl.v1")
}
pub fn factory_proxy_salt() -> B256 {
keccak256("libid.factory.v1")
}
pub fn factory_impl_init_code(artifacts: &Artifacts) -> Result<Bytes> {
artifacts.bytecode("LibidFactory")
}
pub fn predict_factory_impl_address(artifacts: &Artifacts) -> Result<Address> {
let init_code = factory_impl_init_code(artifacts)?;
Ok(CREATE2_DEPLOYER.create2(factory_impl_salt(), keccak256(&init_code)))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FactoryGenesis {
pub admin: Address,
}
impl FactoryGenesis {
pub fn proxy_init_code(&self, artifacts: &Artifacts) -> Result<Bytes> {
let impl_addr = predict_factory_impl_address(artifacts)?;
let init_data = LibidFactory::initializeCall { owner_: self.admin }.abi_encode();
let mut code = artifacts.bytecode("ERC1967Proxy")?.to_vec();
code.extend_from_slice(&(impl_addr, Bytes::from(init_data)).abi_encode_params());
Ok(code.into())
}
pub fn address(&self, artifacts: &Artifacts) -> Result<Address> {
let init_code = self.proxy_init_code(artifacts)?;
Ok(CREATE2_DEPLOYER.create2(factory_proxy_salt(), keccak256(&init_code)))
}
pub async fn ensure<P: Provider>(
&self,
provider: &P,
artifacts: &Artifacts,
) -> Result<Address> {
let factory = self.address(artifacts)?;
let code = provider
.get_code_at(factory)
.await
.map_err(|e| Error::Rpc {
detail: format!("failed to read code at the factory address: {e}"),
})?;
if !code.is_empty() {
return Ok(factory);
}
ensure_create2_deployer(provider).await?;
let impl_addr = predict_factory_impl_address(artifacts)?;
let impl_code =
provider
.get_code_at(impl_addr)
.await
.map_err(|e| Error::Rpc {
detail: format!(
"failed to read code at the factory impl address: {e}"
),
})?;
if impl_code.is_empty() {
deploy_via_create2(
provider,
factory_impl_salt(),
&factory_impl_init_code(artifacts)?,
impl_addr,
"LibidFactory (impl)",
None,
)
.await?;
}
deploy_via_create2(
provider,
factory_proxy_salt(),
&self.proxy_init_code(artifacts)?,
factory,
"LibidFactory (proxy)",
None,
)
.await?;
Ok(factory)
}
}
pub fn predict_address(factory: Address, name: &str) -> Address {
let salt = keccak256(name.as_bytes());
let create3_proxy = factory.create2(salt, keccak256(CREATE3_PROXY_INITCODE));
create3_proxy.create(1)
}
pub async fn ensure_create2_deployer<P: Provider>(provider: &P) -> Result<()> {
let code = provider
.get_code_at(CREATE2_DEPLOYER)
.await
.map_err(|e| Error::Rpc {
detail: format!("failed to read code at the CREATE2 deployer: {e}"),
})?;
if !code.is_empty() {
return Ok(());
}
let balance = provider
.get_balance(CREATE2_DEPLOYER_SIGNER)
.await
.map_err(|e| Error::Rpc {
detail: format!("failed to read the CREATE2 deployer signer balance: {e}"),
})?;
let needed = U256::from(CREATE2_DEPLOYER_FUNDING_WEI);
if balance < needed {
let tx = TransactionRequest::default()
.with_to(CREATE2_DEPLOYER_SIGNER)
.with_value(needed - balance);
let pending = provider
.send_transaction(tx)
.await
.map_err(|e| Error::Rpc {
detail: format!("failed to fund the CREATE2 deployer signer: {e}"),
})?;
pending.get_receipt().await.map_err(|e| Error::Rpc {
detail: format!("CREATE2 deployer funding confirmation failed: {e}"),
})?;
}
let raw = hex::decode(CREATE2_DEPLOYER_INSTALL_TX).map_err(|e| Error::Rpc {
detail: format!("bad CREATE2 deployer install tx constant: {e}"),
})?;
let pending = provider
.send_raw_transaction(&raw)
.await
.map_err(|e| Error::Rpc {
detail: format!(
"this chain rejected the keyless (pre-EIP-155) install transaction \
for the canonical CREATE2 deployer: {e}. There is deliberately no \
fallback deployment path — any other route would change the \
factory address and defeat the cross-network guarantee — so this \
network cannot host the deterministic factory and must be \
reconsidered."
),
})?;
pending.get_receipt().await.map_err(|e| Error::Rpc {
detail: format!("CREATE2 deployer install confirmation failed: {e}"),
})?;
let code = provider
.get_code_at(CREATE2_DEPLOYER)
.await
.map_err(|e| Error::Rpc {
detail: format!("failed to re-read code at the CREATE2 deployer: {e}"),
})?;
if code.is_empty() {
return Err(Error::Rpc {
detail: "the CREATE2 deployer install transaction landed but left no code"
.into(),
});
}
Ok(())
}
pub async fn factory_deploy<P: Provider>(
provider: &P,
factory: Address,
name: &str,
creation_code: Bytes,
) -> Result<Address> {
let call = LibidFactory::deployCall {
name: name.to_string(),
creationCode: creation_code,
};
let tx = TransactionRequest::default()
.with_to(factory)
.with_input(Bytes::from(call.abi_encode()));
let pending = provider
.send_transaction(tx)
.await
.map_err(|e| Error::Rpc {
detail: format!("factory deploy of {name} send failed: {e}"),
})?;
pending.get_receipt().await.map_err(|e| Error::Rpc {
detail: format!("factory deploy of {name} confirmation failed: {e}"),
})?;
let contract = LibidFactory::new(factory, provider);
let addr = contract
.deployedAt(name.to_string())
.call()
.await
.map_err(|e| Error::Rpc {
detail: format!("factory deployedAt({name}) read failed: {e}"),
})?;
if addr == Address::ZERO {
return Err(Error::Rpc {
detail: format!("factory deploy of {name} landed no recorded address"),
});
}
Ok(addr)
}