Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove code duplication that led to inconsistencies #116

Merged
merged 6 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 7 additions & 25 deletions main/src/activate.rs
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
47 changes: 32 additions & 15 deletions main/src/check.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// 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, GREY, LAVENDER},
sys, text,
},
CheckConfig, DataFeeOpts,
};
use alloy_primitives::{Address, B256, U256};
use alloy_sol_macro::sol;
Expand Down Expand Up @@ -87,9 +90,7 @@ pub async fn check(cfg: &CheckConfig) -> Result<ContractCheck> {
}

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 })
}

Expand All @@ -112,7 +113,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,
}
}
}
Expand Down Expand Up @@ -148,17 +149,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<String> {
let fee: u64 = (fee / U256::from(1e9)).try_into()?;
fn format_data_fee(fee: U256) -> String {
let Ok(fee): Result<u64, _> = (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 {
Expand Down Expand Up @@ -247,7 +250,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<Http>) -> Result<U256> {
pub async fn check_activate(
code: Bytes,
address: H160,
opts: &DataFeeOpts,
provider: &Provider<Http>,
) -> Result<U256> {
let contract = Address::from(address.to_fixed_bytes());
let data = ArbWasm::activateProgramCall { program: contract }.abi_encode();
let tx = Eip1559TransactionRequest::new()
Expand All @@ -256,8 +264,17 @@ pub async fn check_activate(code: Bytes, address: H160, provider: &Provider<Http
.value(ONE_ETH);
let state = spoof::code(address, code);
let outs = eth_call(tx, state, provider).await??;
let ArbWasm::activateProgramReturn { dataFee, .. } =
ArbWasm::activateProgramCall::abi_decode_returns(&outs, true)?;
let ArbWasm::activateProgramReturn {
dataFee: data_fee, ..
} = ArbWasm::activateProgramCall::abi_decode_returns(&outs, true)?;

let bump = opts.data_fee_bump_percent;
let adjusted_data_fee = data_fee * U256::from(100 + bump) / U256::from(100);
greyln!(
"wasm data fee: {} {GREY}(originally {}{GREY} with {LAVENDER}{bump}%{GREY} bump)",
format_data_fee(adjusted_data_fee),
format_data_fee(data_fee)
);

Ok(dataFee)
Ok(adjusted_data_fee)
}
32 changes: 18 additions & 14 deletions main/src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,13 @@ pub type SignerClient = SignerMiddleware<Provider<Http>, Wallet<SigningKey>>;

/// 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());
Expand All @@ -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 {
Expand All @@ -93,8 +89,16 @@ pub async fn deploy(cfg: DeployConfig) -> Result<()> {

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!"),
}
Expand Down
17 changes: 14 additions & 3 deletions main/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,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,
Expand All @@ -202,6 +201,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<PathBuf>,
Expand All @@ -228,6 +229,9 @@ struct DeployConfig {
/// If not set, uses the default version of the local cargo stylus binary.
#[arg(long)]
cargo_stylus_version: Option<String>,
/// If set, do not activate the program after deploying it
#[arg(long)]
no_activate: bool,
}

#[derive(Args, Clone, Debug)]
Expand Down Expand Up @@ -314,6 +318,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 {
Expand Down
40 changes: 19 additions & 21 deletions main/src/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand Down
5 changes: 4 additions & 1 deletion main/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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,
};
Expand Down
Loading