From 129b8aac7d8ec368109c68b70165f12647d687dd Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:32:35 +0100 Subject: [PATCH] fix: forge script should adhere to `--json` flag (#9404) * adhere to --quiet flag * revert case-specific handling of writing to progress, redundant * handle writing to multiprogress, previously panic * make verification process compatible with --json flag * revert verifaction --json flow, too messy * clean up * revert * handle json correctly for script deployment logs, incl. receipts * avoid incompatible lines with json output * revert unnecessary change * add json and quiet test * address feedback * fix incorrect ordering --- crates/forge/tests/cli/script.rs | 78 +++++++++++++++++++++ crates/script-sequence/src/sequence.rs | 17 ++++- crates/script/src/broadcast.rs | 8 ++- crates/script/src/lib.rs | 9 ++- crates/script/src/multi_sequence.rs | 17 ++++- crates/script/src/progress.rs | 95 +++++++++++++++----------- crates/script/src/receipts.rs | 76 +++++++++++++-------- crates/script/src/simulate.rs | 58 +++++++++------- crates/verify/src/etherscan/mod.rs | 1 - 9 files changed, 257 insertions(+), 102 deletions(-) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index df2a59bdc4dfb..d7776eee2e87f 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -6,6 +6,7 @@ use anvil::{spawn, NodeConfig}; use forge_script_sequence::ScriptSequence; use foundry_test_utils::{ rpc, + snapbox::IntoData, util::{OTHER_SOLC_VERSION, SOLC_VERSION}, ScriptOutcome, ScriptTester, }; @@ -1821,6 +1822,83 @@ Warning: Script contains a transaction to 0x000000000000000000000000000000000000 "#]]); }); +// Asserts that the script runs with expected non-output using `--quiet` flag +forgetest_async!(adheres_to_quiet_flag, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + prj.add_script( + "Foo", + r#" +import "forge-std/Script.sol"; + +contract SimpleScript is Script { + function run() external returns (bool success) { + vm.startBroadcast(); + (success, ) = address(0).call(""); + } +} + "#, + ) + .unwrap(); + + let (_api, handle) = spawn(NodeConfig::test()).await; + + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--sender", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "--broadcast", + "--unlocked", + "--non-interactive", + "--quiet", + ]) + .assert_empty_stdout(); +}); + +// Asserts that the script runs with expected non-output using `--quiet` flag +forgetest_async!(adheres_to_json_flag, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + prj.add_script( + "Foo", + r#" +import "forge-std/Script.sol"; + +contract SimpleScript is Script { + function run() external returns (bool success) { + vm.startBroadcast(); + (success, ) = address(0).call(""); + } +} + "#, + ) + .unwrap(); + + let (_api, handle) = spawn(NodeConfig::test()).await; + + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--sender", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "--broadcast", + "--unlocked", + "--non-interactive", + "--json", + ]) + .assert_success() + .stdout_eq(str![[r#" +{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"0x6080604052600c805462ff00ff191662010001179055348015601f575f5ffd5b506101568061002d5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122060ba6332e526de9b6bc731fb4682b44e42845196324ec33068982984d700cdd964736f6c634300081b0033","output":"0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122060ba6332e526de9b6bc731fb4682b44e42845196324ec33068982984d700cdd964736f6c634300081b0033","gas_used":90639,"gas_limit":1073682810,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":3214,"gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":0,"gas_limit":1056940983,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1056940820,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":24278,"labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} +{"chain":31337,"estimated_gas_price":"2.000000001","estimated_total_gas_used":29005,"estimated_amount_required":"0.000058010000029005"} +{"chain":"anvil-hardhat","status":"success","tx_hash":"0x4f78afe915fceb282c7625a68eb350bc0bf78acb59ad893e5c62b710a37f3156","contract_address":null,"block_number":1,"gas_used":21000,"gas_price":1000000001} +{"status":"success","transactions":"[..]/broadcast/Foo.sol/31337/run-latest.json","sensitive":"[..]/cache/Foo.sol/31337/run-latest.json"} + +"#]].is_jsonlines()); +}); + // https://github.com/foundry-rs/foundry/pull/7742 forgetest_async!(unlocked_no_sender, |prj, cmd| { foundry_test_utils::util::initialize(prj.root()); diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index e34b6d06a8655..235f28f2c4ff4 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -2,7 +2,7 @@ use crate::transaction::TransactionWithMetadata; use alloy_primitives::{hex, map::HashMap, TxHash}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_common::{fs, TransactionMaybeSigned, SELECTOR_LEN}; +use foundry_common::{fs, shell, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; @@ -127,8 +127,19 @@ impl ScriptSequence { } if !silent { - sh_println!("\nTransactions saved to: {}\n", path.display())?; - sh_println!("Sensitive values saved to: {}\n", sensitive_path.display())?; + if shell::is_json() { + sh_println!( + "{}", + serde_json::json!({ + "status": "success", + "transactions": path.display().to_string(), + "sensitive": sensitive_path.display().to_string(), + }) + )?; + } else { + sh_println!("\nTransactions saved to: {}\n", path.display())?; + sh_println!("Sensitive values saved to: {}\n", sensitive_path.display())?; + } } Ok(()) diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 51e8baf5b3be8..207754e5c9526 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -21,7 +21,7 @@ use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_batch_support, has_different_gas_calc}; use foundry_common::{ provider::{get_http_provider, try_get_http_provider, RetryProvider}, - TransactionMaybeSigned, + shell, TransactionMaybeSigned, }; use foundry_config::Config; use futures::{future::join_all, StreamExt}; @@ -429,8 +429,10 @@ impl BundledState { seq_progress.inner.write().finish(); } - sh_println!("\n\n==========================")?; - sh_println!("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; + if !shell::is_json() { + sh_println!("\n\n==========================")?; + sh_println!("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; + } Ok(BroadcastedState { args: self.args, diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index aeea4940a0bed..517eff552fd0a 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -284,7 +284,10 @@ impl ScriptArgs { // Check if there are any missing RPCs and exit early to avoid hard error. if pre_simulation.execution_artifacts.rpc_data.missing_rpc { - sh_println!("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; + if !shell::is_json() { + sh_println!("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; + } + return Ok(()); } @@ -298,7 +301,9 @@ impl ScriptArgs { // Exit early in case user didn't provide any broadcast/verify related flags. if !bundled.args.should_broadcast() { - sh_println!("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?; + if !shell::is_json() { + sh_println!("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?; + } return Ok(()); } diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs index ec2f03ae9855b..e0fd4d1bc7e68 100644 --- a/crates/script/src/multi_sequence.rs +++ b/crates/script/src/multi_sequence.rs @@ -2,7 +2,7 @@ use eyre::{ContextCompat, Result, WrapErr}; use forge_script_sequence::{ now, sig_to_file_name, ScriptSequence, SensitiveScriptSequence, DRY_RUN_DIR, }; -use foundry_common::fs; +use foundry_common::{fs, shell}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; @@ -146,8 +146,19 @@ impl MultiChainSequence { } if !silent { - sh_println!("\nTransactions saved to: {}\n", self.path.display())?; - sh_println!("Sensitive details saved to: {}\n", self.sensitive_path.display())?; + if shell::is_json() { + sh_println!( + "{}", + serde_json::json!({ + "status": "success", + "transactions": self.path.display().to_string(), + "sensitive": self.sensitive_path.display().to_string(), + }) + )?; + } else { + sh_println!("\nTransactions saved to: {}\n", self.path.display())?; + sh_println!("Sensitive details saved to: {}\n", self.sensitive_path.display())?; + } } Ok(()) diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index d9fa53bb33566..bc23fcf677052 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -7,7 +7,7 @@ use alloy_primitives::{ use eyre::Result; use forge_script_sequence::ScriptSequence; use foundry_cli::utils::init_progress; -use foundry_common::provider::RetryProvider; +use foundry_common::{provider::RetryProvider, shell}; use futures::StreamExt; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use parking_lot::RwLock; @@ -31,33 +31,42 @@ pub struct SequenceProgressState { impl SequenceProgressState { pub fn new(sequence_idx: usize, sequence: &ScriptSequence, multi: MultiProgress) -> Self { - let mut template = "{spinner:.green}".to_string(); - write!(template, " Sequence #{} on {}", sequence_idx + 1, Chain::from(sequence.chain)) - .unwrap(); - template.push_str("{msg}"); - - let top_spinner = ProgressBar::new_spinner() - .with_style(ProgressStyle::with_template(&template).unwrap().tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈✅")); - let top_spinner = multi.add(top_spinner); - - let txs = multi.insert_after( - &top_spinner, - init_progress(sequence.transactions.len() as u64, "txes").with_prefix(" "), - ); - - let receipts = multi.insert_after( - &txs, - init_progress(sequence.transactions.len() as u64, "receipts").with_prefix(" "), - ); - - top_spinner.enable_steady_tick(Duration::from_millis(100)); - txs.enable_steady_tick(Duration::from_millis(1000)); - receipts.enable_steady_tick(Duration::from_millis(1000)); - - txs.set_position(sequence.receipts.len() as u64); - receipts.set_position(sequence.receipts.len() as u64); - - let mut state = Self { top_spinner, txs, receipts, tx_spinners: Default::default(), multi }; + let mut state = if shell::is_quiet() || shell::is_json() { + let top_spinner = ProgressBar::hidden(); + let txs = ProgressBar::hidden(); + let receipts = ProgressBar::hidden(); + + Self { top_spinner, txs, receipts, tx_spinners: Default::default(), multi } + } else { + let mut template = "{spinner:.green}".to_string(); + write!(template, " Sequence #{} on {}", sequence_idx + 1, Chain::from(sequence.chain)) + .unwrap(); + template.push_str("{msg}"); + + let top_spinner = ProgressBar::new_spinner().with_style( + ProgressStyle::with_template(&template).unwrap().tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈✅"), + ); + let top_spinner = multi.add(top_spinner); + + let txs = multi.insert_after( + &top_spinner, + init_progress(sequence.transactions.len() as u64, "txes").with_prefix(" "), + ); + + let receipts = multi.insert_after( + &txs, + init_progress(sequence.transactions.len() as u64, "receipts").with_prefix(" "), + ); + + top_spinner.enable_steady_tick(Duration::from_millis(100)); + txs.enable_steady_tick(Duration::from_millis(1000)); + receipts.enable_steady_tick(Duration::from_millis(1000)); + + txs.set_position(sequence.receipts.len() as u64); + receipts.set_position(sequence.receipts.len() as u64); + + Self { top_spinner, txs, receipts, tx_spinners: Default::default(), multi } + }; for tx_hash in sequence.pending.iter() { state.tx_sent(*tx_hash); @@ -71,16 +80,21 @@ impl SequenceProgressState { pub fn tx_sent(&mut self, tx_hash: B256) { // Avoid showing more than 10 spinners. if self.tx_spinners.len() < 10 { - let spinner = ProgressBar::new_spinner() - .with_style( - ProgressStyle::with_template(" {spinner:.green} {msg}") - .unwrap() - .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈"), - ) - .with_message(format!("{} {}", "[Pending]".yellow(), tx_hash)); - - let spinner = self.multi.insert_before(&self.txs, spinner); - spinner.enable_steady_tick(Duration::from_millis(100)); + let spinner = if shell::is_quiet() || shell::is_json() { + ProgressBar::hidden() + } else { + let spinner = ProgressBar::new_spinner() + .with_style( + ProgressStyle::with_template(" {spinner:.green} {msg}") + .unwrap() + .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈"), + ) + .with_message(format!("{} {}", "[Pending]".yellow(), tx_hash)); + + let spinner = self.multi.insert_before(&self.txs, spinner); + spinner.enable_steady_tick(Duration::from_millis(100)); + spinner + }; self.tx_spinners.insert(tx_hash, spinner); } @@ -98,7 +112,10 @@ impl SequenceProgressState { /// Same as finish_tx_spinner but also prints a message to stdout above all other progress bars. pub fn finish_tx_spinner_with_msg(&mut self, tx_hash: B256, msg: &str) -> std::io::Result<()> { self.finish_tx_spinner(tx_hash); - self.multi.println(msg)?; + + if !(shell::is_quiet() || shell::is_json()) { + self.multi.println(msg)?; + } Ok(()) } diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index c11fdd71a92cd..f073e38e607bc 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -3,7 +3,7 @@ use alloy_primitives::{utils::format_units, TxHash, U256}; use alloy_provider::{PendingTransactionBuilder, PendingTransactionError, Provider, WatchTxError}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; -use foundry_common::provider::RetryProvider; +use foundry_common::{provider::RetryProvider, shell}; use std::time::Duration; /// Convenience enum for internal signalling of transaction status @@ -71,31 +71,51 @@ pub async fn check_tx_status( pub fn format_receipt(chain: Chain, receipt: &AnyTransactionReceipt) -> String { let gas_used = receipt.gas_used; let gas_price = receipt.effective_gas_price; - format!( - "\n##### {chain}\n{status} Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n\n", - status = if !receipt.inner.inner.inner.receipt.status.coerce_status() { - "❌ [Failed]" - } else { - "✅ [Success]" - }, - tx_hash = receipt.transaction_hash, - caddr = if let Some(addr) = &receipt.contract_address { - format!("\nContract Address: {}", addr.to_checksum(None)) - } else { - String::new() - }, - bn = receipt.block_number.unwrap_or_default(), - gas = if gas_price == 0 { - format!("Gas Used: {gas_used}") - } else { - let paid = format_units(gas_used.saturating_mul(gas_price), 18) - .unwrap_or_else(|_| "N/A".into()); - let gas_price = format_units(U256::from(gas_price), 9).unwrap_or_else(|_| "N/A".into()); - format!( - "Paid: {} ETH ({gas_used} gas * {} gwei)", - paid.trim_end_matches('0'), - gas_price.trim_end_matches('0').trim_end_matches('.') - ) - }, - ) + let block_number = receipt.block_number.unwrap_or_default(); + let success = receipt.inner.inner.inner.receipt.status.coerce_status(); + + if shell::is_json() { + let _ = sh_println!( + "{}", + serde_json::json!({ + "chain": chain, + "status": if success { + "success" + } else { + "failed" + }, + "tx_hash": receipt.transaction_hash, + "contract_address": receipt.contract_address.map(|addr| addr.to_string()), + "block_number": block_number, + "gas_used": gas_used, + "gas_price": gas_price, + }) + ); + + String::new() + } else { + format!( + "\n##### {chain}\n{status} Hash: {tx_hash:?}{contract_address}\nBlock: {block_number}\n{gas}\n\n", + status = if success { "✅ [Success]" } else { "❌ [Failed]" }, + tx_hash = receipt.transaction_hash, + contract_address = if let Some(addr) = &receipt.contract_address { + format!("\nContract Address: {}", addr.to_checksum(None)) + } else { + String::new() + }, + gas = if gas_price == 0 { + format!("Gas Used: {gas_used}") + } else { + let paid = format_units(gas_used.saturating_mul(gas_price), 18) + .unwrap_or_else(|_| "N/A".into()); + let gas_price = + format_units(U256::from(gas_price), 9).unwrap_or_else(|_| "N/A".into()); + format!( + "Paid: {} ETH ({gas_used} gas * {} gwei)", + paid.trim_end_matches('0'), + gas_price.trim_end_matches('0').trim_end_matches('.') + ) + }, + ) + } } diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 95427225f2026..e833073ac00ff 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -16,7 +16,7 @@ use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; use foundry_cheatcodes::Wallets; use foundry_cli::utils::{has_different_gas_calc, now}; -use foundry_common::ContractData; +use foundry_common::{shell, ContractData}; use foundry_evm::traces::{decode_trace_arena, render_trace_arena}; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; @@ -151,7 +151,7 @@ impl PreSimulationState { }) .collect::>(); - if self.script_config.evm_opts.verbosity > 3 { + if !shell::is_json() && self.script_config.evm_opts.verbosity > 3 { sh_println!("==========================")?; sh_println!("Simulated On-chain Traces:\n")?; } @@ -220,9 +220,11 @@ impl PreSimulationState { async fn build_runners(&self) -> Result> { let rpcs = self.execution_artifacts.rpc_data.total_rpcs.clone(); - let n = rpcs.len(); - let s = if n != 1 { "s" } else { "" }; - sh_println!("\n## Setting up {n} EVM{s}.")?; + if !shell::is_json() { + let n = rpcs.len(); + let s = if n != 1 { "s" } else { "" }; + sh_println!("\n## Setting up {n} EVM{s}.")?; + } let futs = rpcs.into_iter().map(|rpc| async move { let mut script_config = self.script_config.clone(); @@ -348,24 +350,34 @@ impl FilledTransactionsState { provider_info.gas_price()? }; - sh_println!("\n==========================")?; - sh_println!("\nChain {}", provider_info.chain)?; - - sh_println!( - "\nEstimated gas price: {} gwei", - format_units(per_gas, 9) - .unwrap_or_else(|_| "[Could not calculate]".to_string()) - .trim_end_matches('0') - .trim_end_matches('.') - )?; - sh_println!("\nEstimated total gas used for script: {total_gas}")?; - sh_println!( - "\nEstimated amount required: {} ETH", - format_units(total_gas.saturating_mul(per_gas), 18) - .unwrap_or_else(|_| "[Could not calculate]".to_string()) - .trim_end_matches('0') - )?; - sh_println!("\n==========================")?; + let estimated_gas_price_raw = format_units(per_gas, 9) + .unwrap_or_else(|_| "[Could not calculate]".to_string()); + let estimated_gas_price = + estimated_gas_price_raw.trim_end_matches('0').trim_end_matches('.'); + + let estimated_amount_raw = format_units(total_gas.saturating_mul(per_gas), 18) + .unwrap_or_else(|_| "[Could not calculate]".to_string()); + let estimated_amount = estimated_amount_raw.trim_end_matches('0'); + + if !shell::is_json() { + sh_println!("\n==========================")?; + sh_println!("\nChain {}", provider_info.chain)?; + + sh_println!("\nEstimated gas price: {} gwei", estimated_gas_price)?; + sh_println!("\nEstimated total gas used for script: {total_gas}")?; + sh_println!("\nEstimated amount required: {estimated_amount} ETH",)?; + sh_println!("\n==========================")?; + } else { + sh_println!( + "{}", + serde_json::json!({ + "chain": provider_info.chain, + "estimated_gas_price": estimated_gas_price, + "estimated_total_gas_used": total_gas, + "estimated_amount_required": estimated_amount, + }) + )?; + } } } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index ca90129d543bb..78f39fe96dccc 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -139,7 +139,6 @@ impl VerificationProvider for EtherscanVerificationProvider { retry: RETRY_CHECK_ON_VERIFY, verifier: args.verifier, }; - // return check_args.run().await return self.check(check_args).await } } else {