stylus_tools/core/deployment/
deployer.rs1use crate::core::deployment::prelude::DeploymentCalldata;
5use crate::core::deployment::DeploymentError;
6use crate::core::deployment::DeploymentError::NoContractAddress;
7use alloy::dyn_abi::{DynSolValue, JsonAbiExt, Specifier};
8use alloy::json_abi::Constructor;
9use alloy::primitives::B256;
10use alloy::rpc::types::TransactionReceipt;
11use alloy::{
12 primitives::{address, Address, U256},
13 providers::Provider,
14 sol,
15 sol_types::SolCall,
16 sol_types::SolEvent,
17};
18use eyre::{Context, ErrReport};
19
20pub const ADDRESS: Address = address!("cEcba2F1DC234f70Dd89F2041029807F8D03A990");
21
22sol! {
23 #[sol(rpc)]
24 interface StylusDeployer {
25 event ContractDeployed(address deployedContract);
26
27 function deploy(
28 bytes calldata bytecode,
29 bytes calldata initData,
30 uint256 initValue,
31 bytes32 salt
32 ) public payable returns (address);
33 }
34
35 function stylus_constructor();
36}
37
38#[derive(Debug, thiserror::Error)]
39pub enum DeployerError {
40 #[error("rpc error: {0}")]
41 Rpc(#[from] alloy::transports::RpcError<alloy::transports::TransportErrorKind>),
42
43 #[error("deployment failed during gas estimation")]
44 GasEstimationFailure,
45}
46
47pub async fn parse_tx_calldata(
48 contract_code: &[u8],
49 constructor: &Constructor,
50 constructor_value: U256,
51 constructor_args: Vec<String>,
52 deployer_salt: B256,
53 provider: &impl Provider,
54) -> Result<Vec<u8>, ErrReport> {
55 let mut arg_values = Vec::<DynSolValue>::with_capacity(constructor_args.len());
56 for (arg, param) in constructor_args.iter().zip(constructor.inputs.iter()) {
57 let ty = param
58 .resolve()
59 .wrap_err_with(|| format!("could not resolve constructor arg: {param}"))?;
60 let value = ty
61 .coerce_str(arg)
62 .wrap_err_with(|| format!("could not parse constructor arg: {param}"))?;
63 arg_values.push(value);
64 }
65
66 let calldata_args = constructor.abi_encode_input_raw(&arg_values)?;
67
68 let mut constructor_calldata = Vec::from(stylus_constructorCall::SELECTOR);
69 constructor_calldata.extend(calldata_args);
70
71 let tx_calldata = StylusDeployer::new(Address::ZERO, provider)
72 .deploy_call(
73 DeploymentCalldata::new(contract_code).into(),
74 constructor_calldata.into(),
75 constructor_value,
76 deployer_salt,
77 )
78 .calldata()
79 .to_vec();
80 Ok(tx_calldata)
81}
82
83pub fn get_address_from_receipt(receipt: &TransactionReceipt) -> Result<Address, DeploymentError> {
85 receipt
86 .clone()
87 .into_inner()
88 .logs()
89 .iter()
90 .find(|log| match log.topics().first() {
91 Some(topic) => topic.0 == StylusDeployer::ContractDeployed::SIGNATURE_HASH,
92 None => false,
93 })
94 .map(|log| {
95 if log.data().data.len() != 32 {
96 return Err(NoContractAddress("from ContractDeployed log".to_string()));
97 }
98 Ok(Address::from_slice(&log.data().data[12..32]))
99 })
100 .unwrap_or_else(|| Err(NoContractAddress("from receipt logs".to_string())))
101}