11//! Deploy pipeline: orchestrates the full deployment flow.
22
3- use crate :: config:: DeployConfig ;
4- use crate :: contracts;
5- use crate :: deploy:: create2:: { compute_address, DETERMINISTIC_DEPLOYER } ;
6- use crate :: deploy:: deployer:: ChainDeployer ;
7- use crate :: deploy:: state:: { ContractState , ContractStatus , DeployState } ;
3+ use crate :: {
4+ config:: DeployConfig ,
5+ contracts,
6+ deploy:: {
7+ create2:: { compute_address, DETERMINISTIC_DEPLOYER } ,
8+ deployer:: ChainDeployer ,
9+ state:: { ContractState , ContractStatus , DeployState } ,
10+ } ,
11+ } ;
812use alloy_primitives:: { Address , B256 } ;
913use std:: path:: { Path , PathBuf } ;
1014
@@ -68,31 +72,25 @@ pub(crate) async fn run(
6872 deploy_contract (
6973 deployer,
7074 & mut state,
71- "admin_proxy" ,
72- address,
73- salt,
74- & initcode,
75- contracts:: admin_proxy:: ADMIN_PROXY_BYTECODE ,
76- & pipeline_cfg. state_path ,
75+ & DeployContractParams {
76+ name : "admin_proxy" ,
77+ address,
78+ salt,
79+ initcode : & initcode,
80+ expected_runtime : contracts:: admin_proxy:: ADMIN_PROXY_BYTECODE ,
81+ state_path : & pipeline_cfg. state_path ,
82+ } ,
7783 )
7884 . await ?;
7985 } else {
8086 eprintln ! ( "[3/5] AdminProxy not configured, skipping" ) ;
8187 }
8288
8389 // ── Step 3: Deploy Permit2 ──
84- if pipeline_cfg. config . contracts . permit2 . is_some ( ) {
90+ if let Some ( ref p2_config ) = pipeline_cfg. config . contracts . permit2 {
8591 eprintln ! ( "[4/5] Deploying Permit2..." ) ;
8692
87- if pipeline_cfg
88- . config
89- . contracts
90- . permit2
91- . as_ref ( )
92- . unwrap ( )
93- . address
94- . is_some ( )
95- {
93+ if p2_config. address . is_some ( ) {
9694 eprintln ! ( " WARN: contracts.permit2.address is ignored in deploy mode" ) ;
9795 }
9896
@@ -104,12 +102,14 @@ pub(crate) async fn run(
104102 deploy_contract (
105103 deployer,
106104 & mut state,
107- "permit2" ,
108- address,
109- salt,
110- & initcode,
111- & expected_runtime,
112- & pipeline_cfg. state_path ,
105+ & DeployContractParams {
106+ name : "permit2" ,
107+ address,
108+ salt,
109+ initcode : & initcode,
110+ expected_runtime : & expected_runtime,
111+ state_path : & pipeline_cfg. state_path ,
112+ } ,
113113 )
114114 . await ?;
115115 } else {
@@ -139,25 +139,38 @@ pub(crate) async fn run(
139139 Ok ( ( ) )
140140}
141141
142- /// Build AdminProxy initcode with constructor argument.
142+ /// Build ` AdminProxy` initcode with constructor argument.
143143fn build_admin_proxy_initcode ( owner : Address ) -> Vec < u8 > {
144144 let mut initcode = contracts:: admin_proxy:: ADMIN_PROXY_INITCODE . to_vec ( ) ;
145145 // ABI-encode the owner address as a 32-byte word and append
146146 initcode. extend_from_slice ( owner. into_word ( ) . as_slice ( ) ) ;
147147 initcode
148148}
149149
150+ /// Parameters for deploying a single contract.
151+ struct DeployContractParams < ' a > {
152+ name : & ' a str ,
153+ address : Address ,
154+ salt : B256 ,
155+ initcode : & ' a [ u8 ] ,
156+ expected_runtime : & ' a [ u8 ] ,
157+ state_path : & ' a Path ,
158+ }
159+
150160/// Deploy a single contract via CREATE2 with idempotency.
151161async fn deploy_contract (
152162 deployer : & dyn ChainDeployer ,
153163 state : & mut DeployState ,
154- name : & str ,
155- address : Address ,
156- salt : B256 ,
157- initcode : & [ u8 ] ,
158- expected_runtime : & [ u8 ] ,
159- state_path : & Path ,
164+ params : & DeployContractParams < ' _ > ,
160165) -> eyre:: Result < ( ) > {
166+ let DeployContractParams {
167+ name,
168+ address,
169+ salt,
170+ initcode,
171+ expected_runtime,
172+ state_path,
173+ } = params;
161174 // Check if already deployed or verified in state
162175 let current_status = get_contract_status ( state, name) ;
163176 if current_status >= Some ( ContractStatus :: Deployed ) {
@@ -166,32 +179,31 @@ async fn deploy_contract(
166179 }
167180
168181 // Idempotency: check if code already exists on-chain
169- let existing_code = deployer. get_code ( address) . await ?;
182+ let existing_code = deployer. get_code ( * address) . await ?;
170183 if !existing_code. is_empty ( ) {
171- if existing_code. as_ref ( ) == expected_runtime {
184+ if existing_code. as_ref ( ) == * expected_runtime {
172185 eprintln ! ( " found matching bytecode at {address}, marking as deployed" ) ;
173186 set_contract_state (
174187 state,
175188 name,
176189 ContractState {
177190 status : ContractStatus :: Deployed ,
178- address,
191+ address : * address ,
179192 deploy_tx : None ,
180193 } ,
181194 ) ;
182195 state. save ( state_path) ?;
183196 return Ok ( ( ) ) ;
184- } else {
185- eyre:: bail!(
186- "unexpected bytecode at {address}: expected {} bytes, found {} bytes" ,
187- expected_runtime. len( ) ,
188- existing_code. len( )
189- ) ;
190197 }
198+ eyre:: bail!(
199+ "unexpected bytecode at {address}: expected {} bytes, found {} bytes" ,
200+ expected_runtime. len( ) ,
201+ existing_code. len( )
202+ ) ;
191203 }
192204
193205 // Deploy
194- let receipt = deployer. deploy_create2 ( salt, initcode) . await ?;
206+ let receipt = deployer. deploy_create2 ( * salt, initcode) . await ?;
195207 eyre:: ensure!(
196208 receipt. success,
197209 "CREATE2 deploy tx reverted for {name}: tx={}" ,
@@ -205,7 +217,7 @@ async fn deploy_contract(
205217 name,
206218 ContractState {
207219 status : ContractStatus :: Deployed ,
208- address,
220+ address : * address ,
209221 deploy_tx : Some ( receipt. tx_hash ) ,
210222 } ,
211223 ) ;
@@ -294,12 +306,10 @@ fn build_deploy_manifest(state: &DeployState) -> serde_json::Value {
294306#[ cfg( test) ]
295307mod tests {
296308 use super :: * ;
297- use crate :: config:: * ;
298- use crate :: deploy:: deployer:: TxReceipt ;
309+ use crate :: { config:: * , deploy:: deployer:: TxReceipt } ;
299310 use alloy_primitives:: { address, Bytes } ;
300311 use async_trait:: async_trait;
301- use std:: collections:: HashMap ;
302- use std:: sync:: Mutex ;
312+ use std:: { collections:: HashMap , sync:: Mutex } ;
303313
304314 /// Mock deployer for testing the pipeline without a live chain.
305315 struct MockDeployer {
0 commit comments