1use alloy::{
6 network::TransactionBuilder,
7 primitives::{
8 Address,
9 Bytes,
10 B256,
11 },
12 providers::Provider,
13 rpc::types::TransactionRequest,
14 sol_types::SolCall,
15};
16
17use crate::{
18 artifacts::Artifacts,
19 bindings::proxy::IUUPSUpgradeable,
20 error::{
21 Error,
22 Result,
23 },
24 factory::CREATE2_DEPLOYER,
25};
26
27#[macro_export]
37macro_rules! send_with_nonce_retry {
38 ($call_expr:expr, $label:expr, $provider:expr, $sender:expr) => {{
39 const MAX_RETRIES: u32 = 3;
40 let mut result: $crate::Result<alloy::rpc::types::TransactionReceipt> =
41 Err($crate::Error::Rpc {
42 detail: "unreachable".into(),
43 });
44 for attempt in 0..MAX_RETRIES {
45 let nonce =
46 alloy::providers::Provider::get_transaction_count($provider, $sender)
47 .await
48 .map_err(|e| $crate::Error::Rpc {
49 detail: format!("{} failed to fetch nonce: {e}", $label),
50 })?;
51 match ($call_expr).nonce(nonce).send().await {
52 Ok(pending) => {
53 result =
54 pending.get_receipt().await.map_err(|e| $crate::Error::Rpc {
55 detail: format!("{} confirmation failed: {e}", $label),
56 });
57 break;
58 }
59 Err(e) => {
60 let msg = e.to_string();
61 let next_attempt = attempt.saturating_add(1);
62 if msg.contains("nonce too low") && next_attempt < MAX_RETRIES {
63 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
64 continue;
65 }
66 result = Err($crate::Error::Rpc {
67 detail: format!("{} send failed: {e}", $label),
68 });
69 break;
70 }
71 }
72 }
73 result
74 }};
75}
76
77pub async fn deploy_contract<P: Provider>(
79 provider: &P,
80 bytecode: Bytes,
81 label: &str,
82) -> Result<Address> {
83 deploy_contract_from(provider, bytecode, label, None).await
84}
85
86pub async fn deploy_contract_from<P: Provider>(
91 provider: &P,
92 bytecode: Bytes,
93 label: &str,
94 sender: Option<Address>,
95) -> Result<Address> {
96 let mut tx = TransactionRequest::default().with_deploy_code(bytecode);
97
98 if let Some(addr) = sender {
99 let nonce =
100 provider
101 .get_transaction_count(addr)
102 .await
103 .map_err(|e| Error::Rpc {
104 detail: format!("failed to fetch nonce for {label}: {e}"),
105 })?;
106 tx = tx.with_nonce(nonce);
107 }
108
109 let pending = provider
110 .send_transaction(tx)
111 .await
112 .map_err(|e| Error::Rpc {
113 detail: format!("failed to send {label} deploy tx: {e}"),
114 })?;
115
116 let receipt = pending.get_receipt().await.map_err(|e| Error::Rpc {
117 detail: format!("failed to get {label} deploy receipt: {e}"),
118 })?;
119
120 receipt.contract_address.ok_or_else(|| Error::Rpc {
121 detail: format!("{label} deploy did not return contract address"),
122 })
123}
124
125pub async fn deploy_with_ctor<P: Provider>(
127 provider: &P,
128 bytecode: &Bytes,
129 constructor_args: &[u8],
130 label: &str,
131 sender: Option<Address>,
132) -> Result<Address> {
133 let mut deploy_bytecode = bytecode.to_vec();
134 deploy_bytecode.extend_from_slice(constructor_args);
135 deploy_contract_from(provider, Bytes::from(deploy_bytecode), label, sender).await
136}
137
138pub async fn deploy_proxy<P: Provider>(
141 provider: &P,
142 proxy_bytecode: &Bytes,
143 implementation: Address,
144 init_data: Bytes,
145 label: &str,
146 sender: Option<Address>,
147) -> Result<Address> {
148 let constructor_args =
150 alloy::sol_types::SolValue::abi_encode_params(&(implementation, init_data));
151 deploy_with_ctor(provider, proxy_bytecode, &constructor_args, label, sender).await
152}
153
154pub async fn deploy_behind_proxy<P: Provider, C: SolCall>(
158 provider: &P,
159 artifacts: &Artifacts,
160 contract: &str,
161 init_call: &C,
162 sender: Option<Address>,
163) -> Result<Address> {
164 let implementation = deploy_contract_from(
165 provider,
166 artifacts.bytecode(contract)?,
167 &format!("{contract} (impl)"),
168 sender,
169 )
170 .await?;
171 let proxy_bytecode = artifacts.bytecode("ERC1967Proxy")?;
172 deploy_proxy(
173 provider,
174 &proxy_bytecode,
175 implementation,
176 init_call.abi_encode().into(),
177 &format!("{contract} (proxy)"),
178 sender,
179 )
180 .await
181}
182
183pub async fn upgrade_uups<P: Provider>(
189 provider: &P,
190 artifacts: &Artifacts,
191 proxy: Address,
192 contract: &str,
193 data: Bytes,
194 sender: Option<Address>,
195) -> Result<Address> {
196 let new_impl = deploy_contract_from(
197 provider,
198 artifacts.bytecode(contract)?,
199 &format!("{contract} (new impl)"),
200 sender,
201 )
202 .await?;
203 let proxied = IUUPSUpgradeable::new(proxy, provider);
204 let call = proxied.upgradeToAndCall(new_impl, data);
205 let pending =
206 match sender {
207 Some(addr) => {
208 let nonce = provider.get_transaction_count(addr).await.map_err(|e| {
209 Error::Rpc {
210 detail: format!("{contract} upgrade failed to fetch nonce: {e}"),
211 }
212 })?;
213 call.nonce(nonce).send().await
214 }
215 None => call.send().await,
216 }
217 .map_err(|e| Error::Rpc {
218 detail: format!("{contract} upgradeToAndCall send failed: {e}"),
219 })?;
220 pending.get_receipt().await.map_err(|e| Error::Rpc {
221 detail: format!("{contract} upgradeToAndCall confirmation failed: {e}"),
222 })?;
223 Ok(new_impl)
224}
225
226pub(crate) async fn deploy_via_create2<P: Provider>(
232 provider: &P,
233 salt: B256,
234 init_code: &[u8],
235 predicted: Address,
236 label: &str,
237 sender: Option<Address>,
238) -> Result<()> {
239 let mut input = salt.to_vec();
240 input.extend_from_slice(init_code);
241 let mut tx = TransactionRequest::default()
242 .with_to(CREATE2_DEPLOYER)
243 .with_input(Bytes::from(input));
244 if let Some(addr) = sender {
245 let nonce =
246 provider
247 .get_transaction_count(addr)
248 .await
249 .map_err(|e| Error::Rpc {
250 detail: format!("{label}: failed to fetch nonce: {e}"),
251 })?;
252 tx = tx.with_nonce(nonce);
253 }
254 let pending = provider
255 .send_transaction(tx)
256 .await
257 .map_err(|e| Error::Rpc {
258 detail: format!("{label}: CREATE2 deploy send failed: {e}"),
259 })?;
260 pending.get_receipt().await.map_err(|e| Error::Rpc {
261 detail: format!("{label}: CREATE2 deploy confirmation failed: {e}"),
262 })?;
263 if !code_present(provider, predicted, label).await? {
264 return Err(Error::Rpc {
265 detail: format!("{label}: no code at the predicted address {predicted}"),
266 });
267 }
268 Ok(())
269}
270
271async fn code_present<P: Provider>(
272 provider: &P,
273 address: Address,
274 label: &str,
275) -> Result<bool> {
276 let code = provider
277 .get_code_at(address)
278 .await
279 .map_err(|e| Error::Rpc {
280 detail: format!("{label}: failed to read code at {address}: {e}"),
281 })?;
282 Ok(!code.is_empty())
283}