diff --git a/src/check.rs b/src/check.rs index f3dda61..9270e06 100644 --- a/src/check.rs +++ b/src/check.rs @@ -67,13 +67,15 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result { opt_level: project::OptLevel::default(), nightly: cfg.nightly, rebuild: true, + skip_contract_size_check: cfg.skip_contract_size_check, }) .map_err(|e| eyre!("failed to build project to WASM: {e}"))?, }; println!("Reading WASM file at {}", wasm_file_path.display().grey()); - let (precompressed_bytes, init_code) = project::compress_wasm(&wasm_file_path) - .map_err(|e| eyre!("failed to get compressed WASM bytes: {e}"))?; + let (precompressed_bytes, init_code) = + project::compress_wasm(&wasm_file_path, cfg.skip_contract_size_check) + .map_err(|e| eyre!("failed to get compressed WASM bytes: {e}"))?; let precompressed_size = FileByteSize::new(precompressed_bytes.len() as u64); println!("Uncompressed WASM size: {precompressed_size}"); diff --git a/src/deploy.rs b/src/deploy.rs index 001a633..9f68b63 100644 --- a/src/deploy.rs +++ b/src/deploy.rs @@ -8,7 +8,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use ethers::types::{Eip1559TransactionRequest, H160, U256}; -use ethers::utils::{get_contract_address, to_checksum}; +use ethers::utils::{format_ether, get_contract_address, to_checksum}; use ethers::{middleware::SignerMiddleware, providers::Middleware, signers::Signer}; use eyre::{bail, eyre}; @@ -99,6 +99,7 @@ programs to Stylus chains here https://docs.arbitrum.io/stylus/stylus-quickstart to_checksum(&addr, None), ); } + println!("Address has ETH Balance: {}", format_ether(balance).mint()); } // The folder at which to output the transaction data bytes. @@ -117,10 +118,12 @@ programs to Stylus chains here https://docs.arbitrum.io/stylus/stylus-quickstart opt_level: project::OptLevel::default(), nightly: cfg.check_cfg.nightly, rebuild: false, // The check step at the start of this command rebuilt. + skip_contract_size_check: cfg.check_cfg.skip_contract_size_check, }) .map_err(|e| eyre!("could not build project to WASM: {e}"))?, }; - let (_, init_code) = project::compress_wasm(&wasm_file_path)?; + let (_, init_code) = + project::compress_wasm(&wasm_file_path, cfg.check_cfg.skip_contract_size_check)?; println!(""); println!("{}", "====DEPLOYMENT====".grey()); println!( diff --git a/src/main.rs b/src/main.rs index a74c562..d92ea9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -114,6 +114,10 @@ pub struct CheckConfig { /// Whether to use Rust nightly. #[arg(long)] nightly: bool, + /// Whether to skip the contract size check. In case the contract size is exceeded, a warning + /// will be emitted, but the overall cargo stylus check command will not fail. + #[arg(long)] + skip_contract_size_check: bool, } #[derive(Args, Clone, Debug)] diff --git a/src/project.rs b/src/project.rs index 433e5bf..2188bf4 100644 --- a/src/project.rs +++ b/src/project.rs @@ -23,6 +23,7 @@ pub struct BuildConfig { pub opt_level: OptLevel, pub nightly: bool, pub rebuild: bool, + pub skip_contract_size_check: bool, } #[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] @@ -111,7 +112,7 @@ pub fn build_project_dylib(cfg: BuildConfig) -> Result { }) .ok_or(BuildError::NoWasmFound { path: release_path })?; - if let Err(e) = compress_wasm(&wasm_file_path) { + if let Err(e) = compress_wasm(&wasm_file_path, cfg.skip_contract_size_check) { if let Some(BuildError::MaxCompressedSizeExceeded { got, .. }) = e.downcast_ref() { match cfg.opt_level { OptLevel::S => { @@ -127,6 +128,7 @@ https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md"#, opt_level: OptLevel::Z, nightly: cfg.nightly, rebuild: true, + skip_contract_size_check: cfg.skip_contract_size_check, }); } OptLevel::Z => { @@ -141,6 +143,7 @@ https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md"#, opt_level: OptLevel::Z, nightly: true, rebuild: true, + skip_contract_size_check: cfg.skip_contract_size_check, }); } return Err(BuildError::ExceedsMaxDespiteBestEffort { @@ -157,7 +160,7 @@ https://github.com/OffchainLabs/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md"#, } /// Reads a WASM file at a specified path and returns its brotli compressed bytes. -pub fn compress_wasm(wasm_path: &PathBuf) -> Result<(Vec, Vec)> { +pub fn compress_wasm(wasm_path: &PathBuf, skip_size_check: bool) -> Result<(Vec, Vec)> { let wasm_file_bytes = std::fs::read(wasm_path).map_err(|e| { eyre!( "could not read WASM file at target path {}: {e}", @@ -177,6 +180,10 @@ pub fn compress_wasm(wasm_path: &PathBuf) -> Result<(Vec, Vec)> { let mut deploy_ready_code = hex::decode(EOF_PREFIX).unwrap(); deploy_ready_code.extend(compressed_bytes); + if skip_size_check { + return Ok((wasm_bytes.to_vec(), deploy_ready_code)); + } + let precompressed_size = ByteSize::b(wasm_bytes.len() as u64); if precompressed_size > MAX_PRECOMPRESSED_WASM_SIZE { return Err(BuildError::MaxPrecompressedSizeExceeded { diff --git a/src/tx.rs b/src/tx.rs index e27ddd9..cda2fe6 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -50,20 +50,47 @@ where let base_fee_gwei = format_units(base_fee, "gwei") .map_err(|e| eyre!("could not format base fee as gwei: {e}"))?; println!("Base fee: {} gwei", base_fee_gwei.grey()); - if !(estimate_only) { + if !estimate_only { tx_request.max_fee_per_gas = Some(base_fee); tx_request.max_priority_fee_per_gas = Some(base_fee); } + let address = &tx_request + .from + .ok_or(eyre!("no sender address specified for tx"))?; + let balance = client + .get_balance(*address, None) + .await + .map_err(|e| eyre!("could not get sender balance: {e}"))?; + let typed = TypedTransaction::Eip1559(tx_request.clone()); - let estimated = client - .estimate_gas(&typed, None) + let estimated = client.estimate_gas(&typed, None).await.map_err(|e| { + if e.to_string().contains("gas required exceeds allowance") { + return eyre!( + "not enough funds to transact, only had ETH balance of {}", + format_ether(balance) + ); + } + eyre!("could not estimate gas: {e}") + })?; + + let estimate_gas_price = client + .get_gas_price() .await - .map_err(|e| eyre!("could not estimate gas {e}"))?; + .map_err(|e| eyre!("could not estimate gas price for tx: {e}"))?; + + let total_estimated_cost = estimated + .checked_mul(estimate_gas_price) + .ok_or(eyre!("could not multiply estimated gas cost by gas price"))?; + + let estimate_gas_price = format_units(estimate_gas_price, "gwei") + .map_err(|e| eyre!("could not format gas price to gwei: {e}"))?; println!( - "Estimated gas for {tx_kind}: {} gas units", - estimated.mint() + "Estimations for {tx_kind}: gas price (gwei): {}, gas units: {}, total ETH cost: {}", + estimate_gas_price.mint(), + estimated.mint(), + format_ether(total_estimated_cost).mint(), ); if estimate_only {