From fccba8bf691c307523117e71109f7e7ae7ed122a Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 6 Nov 2024 15:52:59 -0300 Subject: [PATCH 1/5] Add deploy option to disable activate This is useful when we want to run the cargo stylus activate command separately. --- main/src/deploy.rs | 7 +++++++ main/src/main.rs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/main/src/deploy.rs b/main/src/deploy.rs index c6d1887..994fefa 100644 --- a/main/src/deploy.rs +++ b/main/src/deploy.rs @@ -91,6 +91,13 @@ pub async fn deploy(cfg: DeployConfig) -> Result<()> { return Ok(()); } + if cfg.no_activate { + mintln!( + r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: +cargo stylus activate --address {}"#, hex::encode(contract_addr)); + return Ok(()); + } + match contract { ContractCheck::Ready { .. } => { cfg.activate(sender, contract_addr, data_fee, &client) diff --git a/main/src/main.rs b/main/src/main.rs index 59c4b9b..8e9bc11 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -225,6 +225,9 @@ struct DeployConfig { /// If not set, uses the default version of the local cargo stylus binary. #[arg(long)] cargo_stylus_version: Option, + /// If set, do not activate the program after deploying it + #[arg(long)] + no_activate: bool, } #[derive(Args, Clone, Debug)] From a01ae4d6f6a712e1fe4203836dba9e0d0833b9d8 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Wed, 6 Nov 2024 15:59:42 -0300 Subject: [PATCH 2/5] Remove run macro to make code more idiomatic --- main/src/deploy.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/main/src/deploy.rs b/main/src/deploy.rs index 994fefa..fa25430 100644 --- a/main/src/deploy.rs +++ b/main/src/deploy.rs @@ -39,20 +39,13 @@ pub type SignerClient = SignerMiddleware, Wallet>; /// Deploys a stylus contract, activating if needed. pub async fn deploy(cfg: DeployConfig) -> Result<()> { - macro_rules! run { - ($expr:expr) => { - $expr.await? - }; - ($expr:expr, $($msg:expr),+) => { - $expr.await.wrap_err_with(|| eyre!($($msg),+))? - }; - } - - let contract = run!(check::check(&cfg.check_config), "cargo stylus check failed"); + let contract = check::check(&cfg.check_config) + .await + .expect("cargo stylus check failed"); let verbose = cfg.check_config.common_cfg.verbose; let client = sys::new_provider(&cfg.check_config.common_cfg.endpoint)?; - let chain_id = run!(client.get_chainid(), "failed to get chain id"); + let chain_id = client.get_chainid().await.expect("failed to get chain id"); let wallet = cfg.auth.wallet().wrap_err("failed to load wallet")?; let wallet = wallet.with_chain_id(chain_id.as_u64()); @@ -67,7 +60,10 @@ pub async fn deploy(cfg: DeployConfig) -> Result<()> { if let ContractCheck::Ready { .. } = &contract { // check balance early - let balance = run!(client.get_balance(sender, None), "failed to get balance"); + let balance = client + .get_balance(sender, None) + .await + .expect("failed to get balance"); let balance = alloy_ethers_typecast::ethers_u256_to_alloy(balance); if balance < data_fee && !cfg.estimate_gas { @@ -94,7 +90,9 @@ pub async fn deploy(cfg: DeployConfig) -> Result<()> { if cfg.no_activate { mintln!( r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: -cargo stylus activate --address {}"#, hex::encode(contract_addr)); +cargo stylus activate --address {}"#, + hex::encode(contract_addr) + ); return Ok(()); } From b4bd15a489db1d2e6f1b5a5e0a0a121a9773cdc0 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Thu, 7 Nov 2024 11:52:26 -0300 Subject: [PATCH 3/5] Unify the activation fee bump Now the deploy and activate commands have the same option to set the activation fee bump value. --- main/src/activate.rs | 32 +++++++------------------------- main/src/check.rs | 44 +++++++++++++++++++++++++++++--------------- main/src/deploy.rs | 21 ++++++++++----------- main/src/main.rs | 14 +++++++++++--- main/src/verify.rs | 5 ++++- 5 files changed, 61 insertions(+), 55 deletions(-) diff --git a/main/src/activate.rs b/main/src/activate.rs index 8526d8f..ccb59e5 100644 --- a/main/src/activate.rs +++ b/main/src/activate.rs @@ -1,24 +1,22 @@ // Copyright 2023-2024, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/stylus/licenses/COPYRIGHT.md +use crate::check::check_activate; +use crate::constants::ARB_WASM_H160; +use crate::macros::greyln; use crate::util::color::{Color, DebugColor}; use crate::util::sys; +use crate::ActivateConfig; use alloy_primitives::Address; use alloy_sol_macro::sol; use alloy_sol_types::SolCall; use ethers::middleware::{Middleware, SignerMiddleware}; use ethers::signers::Signer; use ethers::types::transaction::eip2718::TypedTransaction; -use ethers::types::{Eip1559TransactionRequest, U256}; +use ethers::types::Eip1559TransactionRequest; use ethers::utils::format_units; use eyre::{bail, Context, Result}; -use crate::check::check_activate; -use crate::constants::ARB_WASM_H160; -use crate::macros::greyln; - -use crate::ActivateConfig; - sol! { interface ArbWasm { function activateProgram(address program) @@ -41,25 +39,14 @@ pub async fn activate_contract(cfg: &ActivateConfig) -> Result<()> { let client = SignerMiddleware::new(provider.clone(), wallet); let code = client.get_code(cfg.address, None).await?; - let data_fee = check_activate(code, cfg.address, &provider).await?; - let mut data_fee = alloy_ethers_typecast::alloy_u256_to_ethers(data_fee); - - greyln!( - "obtained estimated activation data fee {}", - format_units(data_fee, "ether")?.debug_lavender() - ); - greyln!( - "bumping estimated activation data fee by {}%", - cfg.data_fee_bump_percent.debug_lavender() - ); - data_fee = bump_data_fee(data_fee, cfg.data_fee_bump_percent); + let data_fee = check_activate(code, cfg.address, &cfg.data_fee, &provider).await?; let contract: Address = cfg.address.to_fixed_bytes().into(); let data = ArbWasm::activateProgramCall { program: contract }.abi_encode(); let tx = Eip1559TransactionRequest::new() .from(client.address()) .to(*ARB_WASM_H160) - .value(data_fee) + .value(alloy_ethers_typecast::alloy_u256_to_ethers(data_fee)) .data(data); let tx = TypedTransaction::Eip1559(tx); if cfg.estimate_gas { @@ -96,8 +83,3 @@ pub async fn activate_contract(cfg: &ActivateConfig) -> Result<()> { } Ok(()) } - -fn bump_data_fee(fee: U256, pct: u64) -> U256 { - let num = 100 + pct; - fee * U256::from(num) / U256::from(100) -} diff --git a/main/src/check.rs b/main/src/check.rs index 5c31ea6..767153f 100644 --- a/main/src/check.rs +++ b/main/src/check.rs @@ -1,13 +1,13 @@ // Copyright 2023-2024, Offchain Labs, Inc. // For licensing, see https://github.com/OffchainLabs/cargo-stylus/blob/main/licenses/COPYRIGHT.md -use crate::util::{color::Color, sys, text}; use crate::{ check::ArbWasm::ArbWasmErrors, constants::{ARB_WASM_H160, ONE_ETH, TOOLCHAIN_FILE_NAME}, macros::*, project::{self, extract_toolchain_channel, BuildConfig}, - CheckConfig, + util::{color::Color, sys, text}, + CheckConfig, DataFeeOpts, }; use alloy_primitives::{Address, B256, U256}; use alloy_sol_macro::sol; @@ -87,9 +87,7 @@ pub async fn check(cfg: &CheckConfig) -> Result { } let address = cfg.contract_address.unwrap_or(H160::random()); - let fee = check_activate(code.clone().into(), address, &provider).await?; - let visual_fee = format_data_fee(fee).unwrap_or("???".red()); - greyln!("wasm data fee: {visual_fee} ETH"); + let fee = check_activate(code.clone().into(), address, &cfg.data_fee, &provider).await?; Ok(ContractCheck::Ready { code, fee }) } @@ -112,7 +110,7 @@ impl ContractCheck { pub fn suggest_fee(&self) -> U256 { match self { Self::Active { .. } => U256::default(), - Self::Ready { fee, .. } => fee * U256::from(120) / U256::from(100), + Self::Ready { fee, .. } => *fee, } } } @@ -148,17 +146,19 @@ pub fn format_file_size(len: usize, mid: u64, max: u64) -> String { } /// Pretty-prints a data fee. -fn format_data_fee(fee: U256) -> Result { - let fee: u64 = (fee / U256::from(1e9)).try_into()?; +fn format_data_fee(fee: U256) -> String { + let Ok(fee): Result = (fee / U256::from(1e9)).try_into() else { + return ("???").red(); + }; let fee: f64 = fee as f64 / 1e9; - let text = format!("{fee:.6}"); - Ok(if fee <= 5e14 { + let text = format!("{fee:.6} ETH"); + if fee <= 5e14 { text.mint() } else if fee <= 5e15 { text.yellow() } else { text.pink() - }) + } } pub struct EthCallError { @@ -247,7 +247,12 @@ Perhaps the Arbitrum node for the endpoint you are connecting to has not yet upg } /// Checks contract activation, returning the data fee. -pub async fn check_activate(code: Bytes, address: H160, provider: &Provider) -> Result { +pub async fn check_activate( + code: Bytes, + address: H160, + opts: &DataFeeOpts, + provider: &Provider, +) -> Result { let contract = Address::from(address.to_fixed_bytes()); let data = ArbWasm::activateProgramCall { program: contract }.abi_encode(); let tx = Eip1559TransactionRequest::new() @@ -256,8 +261,17 @@ pub async fn check_activate(code: Bytes, address: H160, provider: &Provider Result<()> { return Ok(()); } - if cfg.no_activate { - mintln!( - r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: -cargo stylus activate --address {}"#, - hex::encode(contract_addr) - ); - return Ok(()); - } - match contract { ContractCheck::Ready { .. } => { - cfg.activate(sender, contract_addr, data_fee, &client) - .await? + if cfg.no_activate { + mintln!( + r#"NOTE: You must activate the stylus contract before calling it. To do so, we recommend running: +cargo stylus activate --address {}"#, + hex::encode(contract_addr) + ); + } else { + cfg.activate(sender, contract_addr, data_fee, &client) + .await? + } } ContractCheck::Active { .. } => greyln!("wasm already activated!"), } diff --git a/main/src/main.rs b/main/src/main.rs index 8e9bc11..846ce97 100644 --- a/main/src/main.rs +++ b/main/src/main.rs @@ -181,15 +181,14 @@ pub struct CacheSuggestionsConfig { pub struct ActivateConfig { #[command(flatten)] common_cfg: CommonConfig, + #[command(flatten)] + data_fee: DataFeeOpts, /// Wallet source to use. #[command(flatten)] auth: AuthOpts, /// Deployed Stylus contract address to activate. #[arg(long)] address: H160, - /// Percent to bump the estimated activation data fee by. Default of 20% - #[arg(long, default_value = "20")] - data_fee_bump_percent: u64, /// Whether or not to just estimate gas without sending a tx. #[arg(long)] estimate_gas: bool, @@ -199,6 +198,8 @@ pub struct ActivateConfig { pub struct CheckConfig { #[command(flatten)] common_cfg: CommonConfig, + #[command(flatten)] + data_fee: DataFeeOpts, /// The WASM to check (defaults to any found in the current directory). #[arg(long)] wasm_file: Option, @@ -314,6 +315,13 @@ pub struct SimulateArgs { use_native_tracer: bool, } +#[derive(Clone, Debug, Args)] +struct DataFeeOpts { + /// Percent to bump the estimated activation data fee by. + #[arg(long, default_value = "20")] + data_fee_bump_percent: u64, +} + #[derive(Clone, Debug, Args)] #[clap(group(ArgGroup::new("key").required(true).args(&["private_key_path", "private_key", "keystore_path"])))] struct AuthOpts { diff --git a/main/src/verify.rs b/main/src/verify.rs index c65223b..3ba5f68 100644 --- a/main/src/verify.rs +++ b/main/src/verify.rs @@ -18,7 +18,7 @@ use crate::{ constants::TOOLCHAIN_FILE_NAME, deploy::{self, extract_compressed_wasm, extract_contract_evm_deployment_prelude}, project::{self, extract_toolchain_channel}, - CheckConfig, VerifyConfig, + CheckConfig, DataFeeOpts, VerifyConfig, }; #[derive(Debug, Deserialize, Serialize)] @@ -52,6 +52,9 @@ pub async fn verify(cfg: VerifyConfig) -> eyre::Result<()> { } let check_cfg = CheckConfig { common_cfg: cfg.common_cfg.clone(), + data_fee: DataFeeOpts { + data_fee_bump_percent: 20, + }, wasm_file: None, contract_address: None, }; From 4c9472ce51b9d922497c2bfb05614bcbb260e1a8 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 8 Nov 2024 11:41:44 -0300 Subject: [PATCH 4/5] Unify the code for new and init commands * The new command creates the project directory and calls the init command. * The init code removes the git origin from the project directory. --- main/src/new.rs | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/main/src/new.rs b/main/src/new.rs index a9ecc2b..6a1fdf8 100644 --- a/main/src/new.rs +++ b/main/src/new.rs @@ -7,31 +7,18 @@ use crate::util::{ sys, }; use eyre::{bail, Context, Result}; -use std::{env::current_dir, path::Path}; +use std::{env, fs, path::Path}; -/// Creates a new Stylus project in the current directory -pub fn new(name: &Path, minimal: bool) -> Result<()> { - let repo = match minimal { - true => GITHUB_TEMPLATE_REPO_MINIMAL, - false => GITHUB_TEMPLATE_REPO, - }; - let output = sys::new_command("git") - .arg("clone") - .arg(repo) - .arg(name) - .output() - .wrap_err("git clone failed")?; - - if !output.status.success() { - bail!("git clone command failed"); - } - let path = current_dir().wrap_err("no current dir")?.join(name); - println!("{GREY}new project at: {}", path.to_string_lossy().mint()); - Ok(()) +/// Creates a new directory given the path and then initialize a stylus project. +pub fn new(path: &Path, minimal: bool) -> Result<()> { + fs::create_dir_all(path).wrap_err("failed to create project dir")?; + env::set_current_dir(path).wrap_err("failed to set project dir")?; + init(minimal) } +/// Creates a new Stylus project in the current directory. pub fn init(minimal: bool) -> Result<()> { - let current_dir = current_dir().wrap_err("no current dir")?; + let current_dir = env::current_dir().wrap_err("no current dir")?; let repo = if minimal { GITHUB_TEMPLATE_REPO_MINIMAL } else { @@ -51,6 +38,17 @@ pub fn init(minimal: bool) -> Result<()> { bail!("git clone command failed"); } + let output = sys::new_command("git") + .arg("remote") + .arg("remove") + .arg("origin") + .output() + .wrap_err("git remote remove failed")?; + + if !output.status.success() { + bail!("git remote remove command failed"); + } + println!( "{GREY}initialized project in: {}", current_dir.to_string_lossy().mint() From 22d944c34e51b75334c239a2cac0da26cba24061 Mon Sep 17 00:00:00 2001 From: Gabriel de Quadros Ligneul Date: Fri, 8 Nov 2024 11:55:16 -0300 Subject: [PATCH 5/5] Improve activation-fee log message --- main/src/check.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/main/src/check.rs b/main/src/check.rs index 767153f..9c171a2 100644 --- a/main/src/check.rs +++ b/main/src/check.rs @@ -6,7 +6,10 @@ use crate::{ constants::{ARB_WASM_H160, ONE_ETH, TOOLCHAIN_FILE_NAME}, macros::*, project::{self, extract_toolchain_channel, BuildConfig}, - util::{color::Color, sys, text}, + util::{ + color::{Color, GREY, LAVENDER}, + sys, text, + }, CheckConfig, DataFeeOpts, }; use alloy_primitives::{Address, B256, U256}; @@ -267,11 +270,11 @@ pub async fn check_activate( let bump = opts.data_fee_bump_percent; let adjusted_data_fee = data_fee * U256::from(100 + bump) / U256::from(100); - print!("{}", "wasm data fee: ".grey()); - print!("{}", format_data_fee(adjusted_data_fee)); - print!("{}", " (originally ".grey()); - print!("{}", format_data_fee(data_fee)); - greyln!(" with {bump}% bump)"); + greyln!( + "wasm data fee: {} {GREY}(originally {}{GREY} with {LAVENDER}{bump}%{GREY} bump)", + format_data_fee(adjusted_data_fee), + format_data_fee(data_fee) + ); Ok(adjusted_data_fee) }