From f09d66875b04cd6cc0f208ea71d4636c92e956f9 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Mon, 28 Aug 2023 10:00:08 -0700 Subject: [PATCH] migrate to using eyre everywhere, add this error --- Cargo.lock | 1 + Cargo.toml | 1 + README.md | 68 +++------------------------------------- src/check.rs | 79 ++++++++++++++++++++++++++--------------------- src/constants.rs | 2 ++ src/deploy.rs | 17 +++++----- src/export_abi.rs | 9 +++--- src/main.rs | 2 +- src/new.rs | 31 ++++++++++--------- src/project.rs | 40 ++++++++++++++---------- src/tx.rs | 21 +++++++------ src/wallet.rs | 19 ++++++------ 12 files changed, 128 insertions(+), 162 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a122e8..bfa8d44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,6 +408,7 @@ dependencies = [ "rustc-host", "serde", "serde_json", + "thiserror", "tokio", "wasmer", ] diff --git a/Cargo.toml b/Cargo.toml index bed9908..da3c831 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ ethers = "2.0.8" serde_json = "1.0.103" tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread" ] } wasmer = "3.1.0" +thiserror = "1.0.47" diff --git a/README.md b/README.md index 045ac3d..f2c02d0 100644 --- a/README.md +++ b/README.md @@ -129,31 +129,10 @@ Instruments a Rust project using Stylus. This command runs compiled WASM code th ``` Usage: cargo stylus check [OPTIONS] - -Options: - -e, --endpoint - The endpoint of the L2 node to connect to [default: http://localhost:8545] - --wasm-file-path - If desired, it loads a WASM file from a specified path. If not provided, it will try - to find a WASM file under the current working directory's Rust target release - directory and use its contents for the deploy command - --expected-program-address - Specify the program address we want to check activation for. If unspecified, it - will compute the next program address from the user's wallet address and nonce, - which will require wallet-related flags to be specified - [default: 0x0000000000000000000000000000000000000000] - --private-key-path - Privkey source to use with the cargo stylus plugin - --keystore-path - Path to an Ethereum wallet keystore file, such as the one produced by wallets such as clef - --keystore-password-path - Path to a text file containing a password to the specified wallet keystore file - --nightly - Whether or not to compile the Rust program using the nightly Rust version. - Nightly can help with reducing compressed WASM sizes, however, can be - a security risk if used liberally ``` +See `--help` for all available flags and default values. + ## Deploying Stylus Programs **cargo stylus deploy** @@ -162,39 +141,10 @@ Instruments a Rust project using Stylus and by outputting its brotli-compressed ``` Usage: cargo stylus deploy [OPTIONS] - -Options: - -e, --endpoint - The endpoint of the L2 node to connect to [default: http://localhost:8545] - --wasm-file-path - If desired, it loads a WASM file from a specified path. If not provided, it will try to find a WASM - file under the current working directory's Rust target release directory and - use its contents for the deploy command - --expected-program-address - Specify the program address we want to check activation for. If unspecified, it - will compute the next program address from the user's wallet address and nonce, - which will require wallet-related flags to be specified - [default: 0x0000000000000000000000000000000000000000] - --private-key-path - Privkey source to use with the cargo stylus plugin - --keystore-path - Path to an Ethereum wallet keystore file, such as the one produced by wallets such as clef - --keystore-password-path - Path to a text file containing a password to the specified wallet keystore file - --nightly - Whether or not to compile the Rust program using the nightly Rust version. - Nightly can help with reducing compressed WASM sizes, however, can be a security risk if used liberally - --estimate-gas-only - Does not submit a transaction, but instead estimates the gas required to complete the operation - --mode - By default, submits two transactions to deploy and activate the program - to Arbitrum. Otherwise, a user could choose to split up the deploy and activate - steps into individual transactions [possible values: deploy-only, activate-only] - --activate-program-address - If only activating an already-deployed, onchain program, the address - of the program to send an activation tx for ``` +See `--help` for all available flags and default values. + ## Deploying Non-Rust WASM Projects The Stylus tool can also be used to deploy non-Rust, WASM projects to Stylus by specifying the WASM file directly with the `--wasm-file-path` flag to any of the cargo stylus commands. @@ -230,16 +180,6 @@ We recommend optimizing your Stylus program's sizes to smaller sizes, but keep i For a deep-dive into the different options for optimizing binary sizes using cargo stylus, see [OPTIMIZING_BINARIES.md](./OPTIMIZING_BINARIES.md). -## Alternative Installations - -### Docker Images - -TODO: - -### Precompiled Binaries - -TODO: - ## License Cargo Stylus is distributed under the terms of both the MIT license and the Apache License (Version 2.0). diff --git a/src/check.rs b/src/check.rs index 369df95..8b39490 100644 --- a/src/check.rs +++ b/src/check.rs @@ -1,33 +1,34 @@ // Copyright 2023, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md use bytesize::ByteSize; -use std::path::PathBuf; -use std::str::FromStr; - -use crate::{ - color::Color, - constants::{ARB_WASM_ADDRESS, MAX_PROGRAM_SIZE}, - deploy::activation_calldata, - project::{self, BuildConfig}, - wallet, CheckConfig, -}; use ethers::prelude::*; use ethers::utils::get_contract_address; use ethers::{ providers::JsonRpcClient, types::{transaction::eip2718::TypedTransaction, Address}, }; +use std::path::PathBuf; +use std::str::FromStr; use ethers::types::Eip1559TransactionRequest; use ethers::{ core::types::spoof, providers::{Provider, RawCall}, }; +use eyre::eyre; + +use crate::{ + color::Color, + constants::{ARB_WASM_ADDRESS, MAX_PRECOMPRESSED_WASM_SIZE, MAX_PROGRAM_SIZE}, + deploy::activation_calldata, + project::{self, BuildConfig}, + wallet, CheckConfig, +}; /// Runs a series of checks on the WASM program to ensure it is valid for compilation /// and code size before being deployed and activated onchain. An optional list of checks /// to disable can be specified. -pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<(), String> { +pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<()> { let wasm_file_path: PathBuf = match cfg.wasm_file_path { Some(path) => PathBuf::from_str(&path).unwrap(), None => project::build_project_to_wasm(BuildConfig { @@ -35,29 +36,39 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<(), String> { nightly: cfg.nightly, clean: true, }) - .map_err(|e| format!("failed to build project to WASM: {e}"))?, + .map_err(|e| eyre!("failed to build project to WASM: {e}"))?, }; println!("Reading WASM file at {}", wasm_file_path.display().grey()); - let (_, deploy_ready_code) = project::get_compressed_wasm_bytes(&wasm_file_path) - .map_err(|e| format!("failed to get compressed WASM bytes: {e}"))?; + let (precompressed_bytes, deploy_ready_code) = + project::get_compressed_wasm_bytes(&wasm_file_path) + .map_err(|e| eyre!("failed to get compressed WASM bytes: {e}"))?; - let compressed_size = ByteSize::b(deploy_ready_code.len() as u64); - if compressed_size > MAX_PROGRAM_SIZE { - return Err(format!( - "brotli-compressed WASM size {} is bigger than program size limit: {}", - compressed_size.to_string().red(), - MAX_PROGRAM_SIZE, - )); - } + // let precompressed_size = ByteSize::b(precompressed_bytes.len() as u64); + // if precompressed_size > MAX_PRECOMPRESSED_WASM_SIZE { + // return Err(format!( + // "pre-compressed WASM program size {} is bigger than program size limit: {}", + // precompressed_size.to_string().red(), + // MAX_PRECOMPRESSED_WASM_SIZE, + // )); + // } - println!( - "Compressed WASM size: {}", - compressed_size.to_string().mint(), - ); + // let compressed_size = ByteSize::b(deploy_ready_code.len() as u64); + // if compressed_size > MAX_PROGRAM_SIZE { + // return Err(format!( + // "brotli-compressed WASM size {} is bigger than program size limit: {}", + // compressed_size.to_string().red(), + // MAX_PROGRAM_SIZE, + // )); + // } + + // println!( + // "Compressed WASM size: {}", + // compressed_size.to_string().mint(), + // ); let provider = Provider::::try_from(&cfg.endpoint) - .map_err(|e| format!("could not initialize provider from http: {e}"))?; + .map_err(|e| eyre!("could not initialize provider from http: {e}"))?; let mut expected_program_addr = cfg.expected_program_address; @@ -67,7 +78,7 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<(), String> { let chain_id = provider .get_chainid() .await - .map_err(|e| format!("could not get chain id {e}"))? + .map_err(|e| eyre!("could not get chain id {e}"))? .as_u64(); let client = SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(chain_id)); @@ -76,7 +87,7 @@ pub async fn run_checks(cfg: CheckConfig) -> eyre::Result<(), String> { let nonce = client .get_transaction_count(addr, None) .await - .map_err(|e| format!("could not get nonce {addr}: {e}"))?; + .map_err(|e| eyre!("could not get nonce {addr}: {e}"))?; expected_program_addr = get_contract_address(wallet.address(), nonce); } @@ -91,7 +102,7 @@ pub async fn check_can_activate( client: Provider, expected_program_address: &Address, compressed_wasm: Vec, -) -> eyre::Result<(), String> +) -> eyre::Result<()> where T: JsonRpcClient + Send + Sync, { @@ -109,13 +120,11 @@ where compressed_wasm.into(), ); let response = client.call_raw(&tx).state(&state).await.map_err(|e| { - format!( - "program predeployment check failed when checking against ARB_WASM_ADDRESS {to}: {e}" - ) + eyre!("program predeployment check failed when checking against ARB_WASM_ADDRESS {to}: {e}") })?; if response.len() < 2 { - return Err(format!( + return Err(eyre!( "Stylus version bytes response too short, expected at least 2 bytes but got: {}", hex::encode(&response) )); @@ -123,7 +132,7 @@ where let n = response.len(); let version_bytes: [u8; 2] = response[n - 2..] .try_into() - .map_err(|e| format!("could not parse Stylus version bytes: {e}"))?; + .map_err(|e| eyre!("could not parse Stylus version bytes: {e}"))?; let version = u16::from_be_bytes(version_bytes); println!("Program succeeded Stylus onchain activation checks with Stylus version: {version}"); Ok(()) diff --git a/src/constants.rs b/src/constants.rs index 3ca4bd6..525d27c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -10,6 +10,8 @@ pub const BROTLI_COMPRESSION_LEVEL: u32 = 11; pub const ARB_WASM_ADDRESS: &str = "0000000000000000000000000000000000000071"; /// Maximum allowed size of a program on Arbitrum (and Ethereum). pub const MAX_PROGRAM_SIZE: ByteSize = ByteSize::kb(24); +/// Maximum allowed size of a precompressed WASM program. +pub const MAX_PRECOMPRESSED_WASM_SIZE: ByteSize = ByteSize::kb(128); /// 4 bytes method selector for the activate method of ArbWasm. pub const ARBWASM_ACTIVATE_METHOD_HASH: &str = "58c780c2"; /// Target for compiled WASM folder in a Rust project diff --git a/src/deploy.rs b/src/deploy.rs index d4692de..aa870c0 100644 --- a/src/deploy.rs +++ b/src/deploy.rs @@ -10,6 +10,7 @@ use ethers::{ providers::{Http, Middleware, Provider}, signers::Signer, }; +use eyre::eyre; use crate::project::BuildConfig; use crate::{constants, project, tx, wallet, DeployConfig, DeployMode}; @@ -18,12 +19,12 @@ use crate::{constants, project, tx, wallet, DeployConfig, DeployMode}; /// DeployOnly: Sends a signed tx to deploy a Stylus program to a new address. /// ActivateOnly: Sends a signed tx to activate a Stylus program at a specified address. /// DeployAndActivate (default): Sends both transactions above. -pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> { +pub async fn deploy(cfg: DeployConfig) -> eyre::Result<()> { let wallet = wallet::load(cfg.check_cfg.private_key_path, cfg.check_cfg.keystore_opts) - .map_err(|e| format!("could not load wallet: {e}"))?; + .map_err(|e| eyre!("could not load wallet: {e}"))?; let provider = Provider::::try_from(&cfg.check_cfg.endpoint).map_err(|e| { - format!( + eyre!( "could not initialize provider from http endpoint: {}: {e}", &cfg.check_cfg.endpoint ) @@ -31,7 +32,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> { let chain_id = provider .get_chainid() .await - .map_err(|e| format!("could not get chain id: {e}"))? + .map_err(|e| eyre!("could not get chain id: {e}"))? .as_u64(); let client = SignerMiddleware::new(provider, wallet.clone().with_chain_id(chain_id)); @@ -39,7 +40,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> { let nonce = client .get_transaction_count(addr, None) .await - .map_err(|e| format!("could not get nonce for address {addr}: {e}"))?; + .map_err(|e| eyre!("could not get nonce for address {addr}: {e}"))?; let expected_program_addr = get_contract_address(wallet.address(), nonce); @@ -65,7 +66,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> { nightly: cfg.check_cfg.nightly, clean: false, }) - .map_err(|e| format!("could not build project to WASM: {e}"))?, + .map_err(|e| eyre!("could not build project to WASM: {e}"))?, }; let (_, deploy_ready_code) = project::get_compressed_wasm_bytes(&wasm_file_path)?; println!("Deploying program to address {expected_program_addr:#032x}"); @@ -80,7 +81,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> { &mut tx_request, ) .await - .map_err(|e| format!("could not submit signed deployment tx: {e}"))?; + .map_err(|e| eyre!("could not submit signed deployment tx: {e}"))?; } if activate { let program_addr = cfg @@ -103,7 +104,7 @@ pub async fn deploy(cfg: DeployConfig) -> eyre::Result<(), String> { &mut tx_request, ) .await - .map_err(|e| format!("could not submit signed deployment tx: {e}"))?; + .map_err(|e| eyre!("could not submit signed deployment tx: {e}"))?; } Ok(()) } diff --git a/src/export_abi.rs b/src/export_abi.rs index eb84719..c04c92f 100644 --- a/src/export_abi.rs +++ b/src/export_abi.rs @@ -1,11 +1,12 @@ // Copyright 2023, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md +use eyre::eyre; use std::process::{Command, Stdio}; /// Exports the solidity ABI a Stylus Rust program in the current directory to stdout. -pub fn export_abi(release: bool) -> Result<(), String> { - let target_host = rustc_host::from_cli() - .map_err(|e| format!("could not get host target architecture: {e}"))?; +pub fn export_abi(release: bool) -> eyre::Result<()> { + let target_host = + rustc_host::from_cli().map_err(|e| eyre!("could not get host target architecture: {e}"))?; println!("Exporting Solidity ABI for Stylus Rust program in current directory"); let mut cmd = Command::new("cargo"); @@ -20,6 +21,6 @@ pub fn export_abi(release: bool) -> Result<(), String> { } cmd.output() - .map_err(|e| format!("failed to execute export abi command: {e}"))?; + .map_err(|e| eyre!("failed to execute export abi command: {e}"))?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 993b78b..f55be58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,7 +124,7 @@ pub struct KeystoreOpts { } #[tokio::main] -async fn main() -> eyre::Result<(), String> { +async fn main() -> eyre::Result<()> { let CargoCli::Stylus(args) = CargoCli::parse(); match args.command { diff --git a/src/new.rs b/src/new.rs index c7dad99..a4aff18 100644 --- a/src/new.rs +++ b/src/new.rs @@ -1,5 +1,6 @@ // Copyright 2023, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md +use eyre::eyre; use std::{ env::current_dir, io::Write, @@ -12,11 +13,11 @@ use crate::{color::Color, constants::GITHUB_TEMPLATE_REPOSITORY}; /// Initializes a new Stylus project in the current directory by git cloning /// the stylus-hello-world template repository and renaming /// it to the user's choosing. -pub fn new_stylus_project(name: &str, minimal: bool) -> Result<(), String> { +pub fn new_stylus_project(name: &str, minimal: bool) -> eyre::Result<()> { if name.is_empty() { - return Err("cannot have an empty project name".to_string()); + return Err(eyre!("cannot have an empty project name")); } - let cwd: PathBuf = current_dir().map_err(|e| format!("could not get current dir: {e}"))?; + let cwd: PathBuf = current_dir().map_err(|e| eyre!("could not get current dir: {e}"))?; if minimal { Command::new("cargo") .stdout(Stdio::inherit()) @@ -25,21 +26,21 @@ pub fn new_stylus_project(name: &str, minimal: bool) -> Result<(), String> { .arg("--bin") .arg(name) .output() - .map_err(|e| format!("failed to execute cargo new: {e}"))?; + .map_err(|e| eyre!("failed to execute cargo new: {e}"))?; let cargo_config_dir_path = cwd.join(name).join(".cargo"); std::fs::create_dir_all(&cargo_config_dir_path) - .map_err(|e| format!("could not create .cargo folder in new project: {e}"))?; + .map_err(|e| eyre!("could not create .cargo folder in new project: {e}"))?; let cargo_config_path = cargo_config_dir_path.join("config"); let mut f = std::fs::OpenOptions::new() .create(true) .write(true) .open(cargo_config_path) - .map_err(|e| format!("could not open config file: {e}"))?; + .map_err(|e| eyre!("could not open config file: {e}"))?; f.write_all(cargo_config().as_bytes()) - .map_err(|e| format!("could not write to file: {e}"))?; + .map_err(|e| eyre!("could not write to file: {e}"))?; f.flush() - .map_err(|e| format!("could not write to file: {e}"))?; + .map_err(|e| eyre!("could not write to file: {e}"))?; let main_path = cwd.join(name).join("src").join("main.rs"); @@ -47,22 +48,22 @@ pub fn new_stylus_project(name: &str, minimal: bool) -> Result<(), String> { let mut f = std::fs::OpenOptions::new() .write(true) .open(main_path) - .map_err(|e| format!("could not open main.rs file: {e}"))?; + .map_err(|e| eyre!("could not open main.rs file: {e}"))?; f.write_all(basic_entrypoint().as_bytes()) - .map_err(|e| format!("could not write to file: {e}"))?; + .map_err(|e| eyre!("could not write to file: {e}"))?; f.flush() - .map_err(|e| format!("could not write to file: {e}"))?; + .map_err(|e| eyre!("could not write to file: {e}"))?; // Overwrite the default Cargo.toml file. let cargo_path = cwd.join(name).join("Cargo.toml"); let mut f = std::fs::OpenOptions::new() .write(true) .open(cargo_path) - .map_err(|e| format!("could not open Cargo.toml file: {e}"))?; + .map_err(|e| eyre!("could not open Cargo.toml file: {e}"))?; f.write_all(minimal_cargo_toml(name).as_bytes()) - .map_err(|e| format!("could not write to file: {e}"))?; + .map_err(|e| eyre!("could not write to file: {e}"))?; f.flush() - .map_err(|e| format!("could not write to file: {e}"))?; + .map_err(|e| eyre!("could not write to file: {e}"))?; return Ok(()); } Command::new("git") @@ -72,7 +73,7 @@ pub fn new_stylus_project(name: &str, minimal: bool) -> Result<(), String> { .arg(GITHUB_TEMPLATE_REPOSITORY) .arg(name) .output() - .map_err(|e| format!("failed to execute git clone: {e}"))?; + .map_err(|e| eyre!("failed to execute git clone: {e}"))?; let project_path = cwd.join(name); println!( diff --git a/src/project.rs b/src/project.rs index 87b3894..b2c32d8 100644 --- a/src/project.rs +++ b/src/project.rs @@ -7,6 +7,7 @@ use std::process::{Command, Stdio}; use brotli2::read::BrotliEncoder; use bytesize::ByteSize; +use eyre::eyre; use crate::constants::MAX_PROGRAM_SIZE; use crate::{ @@ -27,9 +28,19 @@ pub struct BuildConfig { pub clean: bool, } +#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] +pub enum BuildError { + #[error("Could not find WASM in release dir ({path})")] + NoWasmFound { path: PathBuf }, + #[error( + "program size exceeds max despite --nightly flag. We recommend splitting up your program" + )] + ExceedsMaxDespiteBestEffort, +} + /// Build a Rust project to WASM and return the path to the compiled WASM file. -pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result { - let cwd: PathBuf = current_dir().map_err(|e| format!("could not get current dir: {e}"))?; +pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result { + let cwd: PathBuf = current_dir().map_err(|e| eyre!("could not get current dir: {e}"))?; if cfg.clean { // Clean the cargo project for fresh checks each time. @@ -38,7 +49,7 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result .stderr(Stdio::inherit()) .arg("clean") .output() - .map_err(|e| format!("failed to execute cargo clean: {e}"))?; + .map_err(|e| eyre!("failed to execute cargo clean: {e}"))?; } let mut cmd = Command::new("cargo"); @@ -69,13 +80,13 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result cmd.arg("--release") .arg(format!("--target={}", RUST_TARGET)) .output() - .map_err(|e| format!("failed to execute cargo build: {e}"))?; + .map_err(|e| eyre!("failed to execute cargo build: {e}"))?; let release_path = cwd.join("target").join(RUST_TARGET).join("release"); // Gets the files in the release folder. - let release_files: Vec = std::fs::read_dir(release_path) - .map_err(|e| format!("could not read release dir: {e}"))? + let release_files: Vec = std::fs::read_dir(&release_path) + .map_err(|e| eyre!("could not read release dir: {e}"))? .filter(|r| r.is_ok()) .map(|r| r.unwrap().path()) .filter(|r| r.is_file()) @@ -89,7 +100,7 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result } false }) - .ok_or("could not find WASM file in release dir")?; + .ok_or(BuildError::NoWasmFound { path: release_path })?; let (_, compressed_wasm_code) = get_compressed_wasm_bytes(&wasm_file_path)?; let compressed_size = ByteSize::b(compressed_wasm_code.len() as u64); @@ -109,17 +120,14 @@ pub fn build_project_to_wasm(cfg: BuildConfig) -> eyre::Result } OptLevel::Z => { if !cfg.nightly { - let msg = format!( + let msg = eyre!( r#"WASM program size {} > max size of 24Kb after applying optimizations. We recommend reducing the codesize or attempting to build again with the --nightly flag. However, this flag can pose a security risk if used liberally"#, compressed_size.red(), ); return Err(msg); } - return Err( - "program size exceeds max despite --nightly flags. We recommend splitting up your program" - .to_string(), - ); + return Err(BuildError::ExceedsMaxDespiteBestEffort.into()); } } } @@ -128,23 +136,23 @@ reducing the codesize or attempting to build again with the --nightly flag. Howe } /// Reads a WASM file at a specified path and returns its brotli compressed bytes. -pub fn get_compressed_wasm_bytes(wasm_path: &PathBuf) -> eyre::Result<(Vec, Vec), String> { +pub fn get_compressed_wasm_bytes(wasm_path: &PathBuf) -> eyre::Result<(Vec, Vec)> { let wasm_file_bytes = std::fs::read(wasm_path).map_err(|e| { - format!( + eyre!( "could not read WASM file at target path {}: {e}", wasm_path.as_os_str().to_string_lossy(), ) })?; let wasm_bytes = wasmer::wat2wasm(&wasm_file_bytes) - .map_err(|e| format!("could not parse wasm file bytes: {e}"))?; + .map_err(|e| eyre!("could not parse wasm file bytes: {e}"))?; let wasm_bytes = &*wasm_bytes; let mut compressor = BrotliEncoder::new(wasm_bytes, BROTLI_COMPRESSION_LEVEL); let mut compressed_bytes = vec![]; compressor .read_to_end(&mut compressed_bytes) - .map_err(|e| format!("could not Brotli compress WASM bytes: {e}"))?; + .map_err(|e| eyre!("could not Brotli compress WASM bytes: {e}"))?; let mut deploy_ready_code = hex::decode(EOF_PREFIX).unwrap(); deploy_ready_code.extend(compressed_bytes); Ok((wasm_bytes.to_vec(), deploy_ready_code)) diff --git a/src/tx.rs b/src/tx.rs index b3c9a72..cfa99b9 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -5,6 +5,7 @@ use crate::color::Color; use ethers::types::transaction::eip2718::TypedTransaction; use ethers::types::Eip1559TransactionRequest; use ethers::{middleware::SignerMiddleware, providers::Middleware, signers::Signer}; +use eyre::eyre; /// Submits a tx to a client given a data payload and a /// transaction request to sign and send. If estimate_only is true, only a call to @@ -14,7 +15,7 @@ pub async fn submit_signed_tx( tx_kind: &str, estimate_only: bool, tx_request: &mut Eip1559TransactionRequest, -) -> eyre::Result<(), String> +) -> eyre::Result<()> where M: Middleware, S: Signer, @@ -22,15 +23,15 @@ where let block_num = client .get_block_number() .await - .map_err(|e| format!("could not get block number: {e}"))?; + .map_err(|e| eyre!("could not get block number: {e}"))?; let block = client .get_block(block_num) .await - .map_err(|e| format!("could not get block: {e}"))? - .ok_or("no block found")?; + .map_err(|e| eyre!("could not get block: {e}"))? + .ok_or(eyre!("no block found"))?; let base_fee = block .base_fee_per_gas - .ok_or("no base fee found for block")?; + .ok_or(eyre!("no base fee found for block"))?; if !(estimate_only) { tx_request.max_fee_per_gas = Some(base_fee); @@ -41,7 +42,7 @@ where let estimated = client .estimate_gas(&typed, None) .await - .map_err(|e| format!("{}", e))?; + .map_err(|e| eyre!("not estimate gas {e}"))?; println!("Estimated gas for {tx_kind}: {}", estimated.pink()); @@ -54,15 +55,15 @@ where let pending_tx = client .send_transaction(typed, None) .await - .map_err(|e| format!("could not send tx: {e}"))?; + .map_err(|e| eyre!("could not send tx: {e}"))?; let receipt = pending_tx .await - .map_err(|e| format!("could not get receipt: {e}"))? - .ok_or("no receipt found")?; + .map_err(|e| eyre!("could not get receipt: {e}"))? + .ok_or(eyre!("no receipt found"))?; match receipt.status { - None => Err(format!( + None => Err(eyre!( "{tx_kind} tx with hash {} reverted", receipt.transaction_hash, )), diff --git a/src/wallet.rs b/src/wallet.rs index e2c5e7a..d2633b6 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -4,6 +4,7 @@ use std::io::{BufRead, BufReader}; use std::str::FromStr; use ethers::signers::LocalWallet; +use eyre::eyre; use crate::KeystoreOpts; @@ -12,38 +13,38 @@ use crate::KeystoreOpts; pub fn load( private_key_path: Option, keystore_opts: KeystoreOpts, -) -> eyre::Result { +) -> eyre::Result { if private_key_path.is_some() && (keystore_opts.keystore_password_path.is_some() && keystore_opts.keystore_path.is_some()) { - return Err("must provide either --private-key-path or (--keystore-path and --keystore-password-path)".to_string()); + return Err(eyre!("must provide either --private-key-path or (--keystore-path and --keystore-password-path)")); } if let Some(priv_key_path) = &private_key_path { let privkey = read_secret_from_file(priv_key_path)?; return LocalWallet::from_str(&privkey) - .map_err(|e| format!("could not parse private key: {e}")); + .map_err(|e| eyre!("could not parse private key: {e}")); } let keystore_password_path = keystore_opts .keystore_password_path .as_ref() - .ok_or("no keystore password path provided")?; + .ok_or(eyre!("no keystore password path provided"))?; let keystore_pass = read_secret_from_file(keystore_password_path)?; let keystore_path = keystore_opts .keystore_path .as_ref() - .ok_or("no keystore path provided")?; + .ok_or(eyre!("no keystore path provided"))?; LocalWallet::decrypt_keystore(keystore_path, keystore_pass) - .map_err(|e| format!("could not decrypt keystore: {e}")) + .map_err(|e| eyre!("could not decrypt keystore: {e}")) } -fn read_secret_from_file(fpath: &str) -> Result { +fn read_secret_from_file(fpath: &str) -> eyre::Result { let f = std::fs::File::open(fpath) - .map_err(|e| format!("could not open file at path: {fpath}: {e}"))?; + .map_err(|e| eyre!("could not open file at path: {fpath}: {e}"))?; let mut buf_reader = BufReader::new(f); let mut secret = String::new(); buf_reader .read_line(&mut secret) - .map_err(|e| format!("could not read secret from file at path {fpath}: {e}"))?; + .map_err(|e| eyre!("could not read secret from file at path {fpath}: {e}"))?; Ok(secret.trim().to_string()) }