1use alloy::{
6 hex,
7 primitives::{
8 Address,
9 Bytes,
10 },
11 providers::Provider,
12 sol_types::SolCall,
13};
14
15use crate::{
16 artifacts::Artifacts,
17 bindings::proxy::IUUPSUpgradeable,
18 error::{
19 Error,
20 Result,
21 },
22};
23
24#[macro_export]
34macro_rules! send_with_nonce_retry {
35 ($call_expr:expr, $label:expr, $provider:expr, $sender:expr) => {{
36 const MAX_RETRIES: u32 = 3;
37 let mut result: $crate::Result<alloy::rpc::types::TransactionReceipt> =
38 Err($crate::Error::Rpc {
39 detail: "unreachable".into(),
40 });
41 for attempt in 0..MAX_RETRIES {
42 let nonce =
43 alloy::providers::Provider::get_transaction_count($provider, $sender)
44 .await
45 .map_err(|e| $crate::Error::Rpc {
46 detail: format!("{} failed to fetch nonce: {e}", $label),
47 })?;
48 match ($call_expr).nonce(nonce).send().await {
49 Ok(pending) => {
50 result =
51 pending.get_receipt().await.map_err(|e| $crate::Error::Rpc {
52 detail: format!("{} confirmation failed: {e}", $label),
53 });
54 break;
55 }
56 Err(e) => {
57 let msg = e.to_string();
58 let next_attempt = attempt.saturating_add(1);
59 if msg.contains("nonce too low") && next_attempt < MAX_RETRIES {
60 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
61 continue;
62 }
63 result = Err($crate::Error::Rpc {
64 detail: format!("{} send failed: {e}", $label),
65 });
66 break;
67 }
68 }
69 }
70 result
71 }};
72}
73
74pub async fn deploy_contract<P: Provider>(
76 provider: &P,
77 bytecode: Bytes,
78 label: &str,
79) -> Result<Address> {
80 deploy_contract_from(provider, bytecode, label, None).await
81}
82
83pub async fn deploy_contract_from<P: Provider>(
88 provider: &P,
89 bytecode: Bytes,
90 label: &str,
91 sender: Option<Address>,
92) -> Result<Address> {
93 use alloy::{
94 network::TransactionBuilder,
95 rpc::types::TransactionRequest,
96 };
97
98 let mut tx = TransactionRequest::default().with_deploy_code(bytecode);
99
100 if let Some(addr) = sender {
101 let nonce =
102 provider
103 .get_transaction_count(addr)
104 .await
105 .map_err(|e| Error::Rpc {
106 detail: format!("failed to fetch nonce for {label}: {e}"),
107 })?;
108 tx = tx.with_nonce(nonce);
109 }
110
111 let pending = provider
112 .send_transaction(tx)
113 .await
114 .map_err(|e| Error::Rpc {
115 detail: format!("failed to send {label} deploy tx: {e}"),
116 })?;
117
118 let receipt = pending.get_receipt().await.map_err(|e| Error::Rpc {
119 detail: format!("failed to get {label} deploy receipt: {e}"),
120 })?;
121
122 receipt.contract_address.ok_or_else(|| Error::Rpc {
123 detail: format!("{label} deploy did not return contract address"),
124 })
125}
126
127pub async fn deploy_with_ctor<P: Provider>(
129 provider: &P,
130 bytecode: &Bytes,
131 constructor_args: &[u8],
132 label: &str,
133 sender: Option<Address>,
134) -> Result<Address> {
135 let mut deploy_bytecode = bytecode.to_vec();
136 deploy_bytecode.extend_from_slice(constructor_args);
137 deploy_contract_from(provider, Bytes::from(deploy_bytecode), label, sender).await
138}
139
140pub async fn deploy_proxy<P: Provider>(
143 provider: &P,
144 proxy_bytecode: &Bytes,
145 implementation: Address,
146 init_data: Bytes,
147 label: &str,
148 sender: Option<Address>,
149) -> Result<Address> {
150 let constructor_args =
152 alloy::sol_types::SolValue::abi_encode_params(&(implementation, init_data));
153 deploy_with_ctor(provider, proxy_bytecode, &constructor_args, label, sender).await
154}
155
156pub async fn deploy_behind_proxy<P: Provider, C: SolCall>(
160 provider: &P,
161 artifacts: &Artifacts,
162 contract: &str,
163 init_call: &C,
164 sender: Option<Address>,
165) -> Result<Address> {
166 let implementation = deploy_contract_from(
167 provider,
168 artifacts.bytecode(contract)?,
169 &format!("{contract} (impl)"),
170 sender,
171 )
172 .await?;
173 let proxy_bytecode = artifacts.bytecode("ERC1967Proxy")?;
174 deploy_proxy(
175 provider,
176 &proxy_bytecode,
177 implementation,
178 init_call.abi_encode().into(),
179 &format!("{contract} (proxy)"),
180 sender,
181 )
182 .await
183}
184
185pub async fn upgrade_uups<P: Provider>(
191 provider: &P,
192 artifacts: &Artifacts,
193 proxy: Address,
194 contract: &str,
195 data: Bytes,
196 sender: Option<Address>,
197) -> Result<Address> {
198 let new_impl = deploy_contract_from(
199 provider,
200 artifacts.bytecode(contract)?,
201 &format!("{contract} (new impl)"),
202 sender,
203 )
204 .await?;
205 let proxied = IUUPSUpgradeable::new(proxy, provider);
206 let call = proxied.upgradeToAndCall(new_impl, data);
207 let pending =
208 match sender {
209 Some(addr) => {
210 let nonce = provider.get_transaction_count(addr).await.map_err(|e| {
211 Error::Rpc {
212 detail: format!("{contract} upgrade failed to fetch nonce: {e}"),
213 }
214 })?;
215 call.nonce(nonce).send().await
216 }
217 None => call.send().await,
218 }
219 .map_err(|e| Error::Rpc {
220 detail: format!("{contract} upgradeToAndCall send failed: {e}"),
221 })?;
222 pending.get_receipt().await.map_err(|e| Error::Rpc {
223 detail: format!("{contract} upgradeToAndCall confirmation failed: {e}"),
224 })?;
225 Ok(new_impl)
226}
227
228pub async fn load_linked_bytecode<P: Provider>(
234 provider: &P,
235 artifacts: &Artifacts,
236 file: &str,
237 contract: &str,
238 sender: Option<Address>,
239) -> Result<Bytes> {
240 let mut hex_str = artifacts.bytecode_hex(file, contract)?;
241
242 for (lib_file, libs) in artifacts.link_references(file, contract)? {
245 let lib_stem = std::path::Path::new(&lib_file)
246 .file_stem()
247 .and_then(|s| s.to_str())
248 .ok_or_else(|| Error::Artifact {
249 detail: format!("bad library file path {lib_file}"),
250 })?;
251 for (lib_name, refs) in libs.as_object().into_iter().flatten() {
252 let lib_bytecode = Box::pin(load_linked_bytecode(
253 provider, artifacts, lib_stem, lib_name, sender,
254 ))
255 .await?;
256 let lib_addr = deploy_contract_from(
257 provider,
258 lib_bytecode,
259 &format!("{lib_name} (library)"),
260 sender,
261 )
262 .await?;
263 let addr_hex = hex::encode(lib_addr.as_slice()); for r in refs.as_array().into_iter().flatten() {
265 let start = r["start"]
266 .as_u64()
267 .and_then(|v| usize::try_from(v).ok())
268 .ok_or_else(|| Error::Artifact {
269 detail: format!("bad linkReference start for {lib_name}"),
270 })?;
271 let length = r["length"]
272 .as_u64()
273 .and_then(|v| usize::try_from(v).ok())
274 .ok_or_else(|| Error::Artifact {
275 detail: format!("bad linkReference length for {lib_name}"),
276 })?;
277 let begin = start.checked_mul(2);
279 let end = start.checked_add(length).and_then(|v| v.checked_mul(2));
280 let (begin, end) = begin.zip(end).ok_or_else(|| Error::Artifact {
281 detail: format!("linkReference offset overflow for {lib_name}"),
282 })?;
283 hex_str.replace_range(begin..end, &addr_hex);
284 }
285 }
286 }
287
288 let bytes = hex::decode(&hex_str).map_err(|e| Error::Artifact {
289 detail: format!("invalid bytecode hex after linking {file}.{contract}: {e}"),
290 })?;
291 Ok(Bytes::from(bytes))
292}