Skip to content

Commit

Permalink
feat: add fee token to TransactionOption and TxnConfig (#2650)
Browse files Browse the repository at this point in the history
* feat: add fee token to TransactionOption and TxnConfig

* fix: fix clap help text

* fix: remove unused file and switch to single match for fee config

* fix: add v3 to multicall and display better error for validation error

* fix: better display of options and update sepolia manifest
  • Loading branch information
glihm authored Nov 7, 2024
1 parent 2050ec4 commit 3db62fa
Show file tree
Hide file tree
Showing 12 changed files with 621 additions and 611 deletions.
2 changes: 1 addition & 1 deletion bin/sozo/src/commands/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl ExecuteArgs {
self.starknet.url(profile_config.env.as_ref())?,
);

let txn_config: TxnConfig = self.transaction.into();
let txn_config: TxnConfig = self.transaction.try_into()?;

config.tokio_handle().block_on(async {
let local_manifest = ws.read_manifest_profile()?;
Expand Down
2 changes: 1 addition & 1 deletion bin/sozo/src/commands/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl MigrateArgs {

let world_address = world_diff.world_info.address;

let mut txn_config: TxnConfig = self.transaction.into();
let mut txn_config: TxnConfig = self.transaction.try_into()?;
txn_config.wait = true;

let migration = Migration::new(
Expand Down
207 changes: 196 additions & 11 deletions bin/sozo/src/commands/options/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,49 @@
use std::fmt::{Display, Formatter};

use anyhow::{bail, Result};
use clap::Args;
use dojo_utils::{TxnAction, TxnConfig};
use clap::builder::PossibleValue;
use clap::{Args, ValueEnum};
use dojo_utils::{EthFeeConfig, FeeConfig, StrkFeeConfig, TxnAction, TxnConfig};
use starknet::core::types::Felt;

#[derive(Debug, Args, Default)]
#[command(next_help_heading = "Transaction options")]
pub struct TransactionOptions {
#[arg(long)]
#[arg(help = "Fee token to use.")]
#[arg(default_value_t = FeeToken::Strk)]
#[arg(global = true)]
pub fee: FeeToken,

#[arg(help_heading = "Transaction options - ETH")]
#[arg(long, value_name = "MULTIPLIER")]
#[arg(help = "The multiplier to use for the fee estimate.")]
#[arg(long_help = "The multiplier to use for the fee estimate. This value will be used on \
the estimated fee which will be used as the max fee for the transaction. \
(max_fee = estimated_fee * multiplier)")]
#[arg(conflicts_with = "max_fee_raw")]
#[arg(conflicts_with_all = ["max_fee_raw", "gas", "gas_price"])]
#[arg(global = true)]
pub fee_estimate_multiplier: Option<f64>,

#[arg(help_heading = "Transaction options - ETH")]
#[arg(long)]
#[arg(help = "Maximum raw value to be used for fees, in Wei.")]
#[arg(conflicts_with = "fee_estimate_multiplier")]
#[arg(conflicts_with_all = ["fee_estimate_multiplier", "gas", "gas_price"])]
#[arg(global = true)]
pub max_fee_raw: Option<Felt>,

#[arg(help_heading = "Transaction options - STRK")]
#[arg(long, help = "Maximum L1 gas amount.")]
#[arg(conflicts_with_all = ["max_fee_raw", "fee_estimate_multiplier"])]
#[arg(global = true)]
pub gas: Option<u64>,

#[arg(help_heading = "Transaction options - STRK")]
#[arg(long, help = "Maximum L1 gas price in STRK.")]
#[arg(conflicts_with_all = ["max_fee_raw", "fee_estimate_multiplier"])]
#[arg(global = true)]
pub gas_price: Option<u128>,

#[arg(long)]
#[arg(help = "Wait until the transaction is accepted by the sequencer, returning the status \
and hash.")]
Expand Down Expand Up @@ -58,22 +81,184 @@ impl TransactionOptions {
(false, false) => Ok(TxnAction::Send {
wait: self.wait || self.walnut,
receipt: self.receipt,
max_fee_raw: self.max_fee_raw,
fee_estimate_multiplier: self.fee_estimate_multiplier,
fee_config: match self.fee {
FeeToken::Strk => {
FeeConfig::Strk(StrkFeeConfig { gas: self.gas, gas_price: self.gas_price })
}
FeeToken::Eth => FeeConfig::Eth(EthFeeConfig {
max_fee_raw: self.max_fee_raw,
fee_estimate_multiplier: self.fee_estimate_multiplier,
}),
},
walnut: self.walnut,
}),
}
}
}

impl From<TransactionOptions> for TxnConfig {
fn from(value: TransactionOptions) -> Self {
Self {
fee_estimate_multiplier: value.fee_estimate_multiplier,
impl TryFrom<TransactionOptions> for TxnConfig {
type Error = anyhow::Error;

fn try_from(value: TransactionOptions) -> Result<Self> {
match value.fee {
FeeToken::Eth => {
if value.gas.is_some() || value.gas_price.is_some() {
bail!(
"Gas and gas price are not supported for ETH transactions. Use `--fee \
strk` instead."
);
}
}
FeeToken::Strk => {
if value.max_fee_raw.is_some() || value.fee_estimate_multiplier.is_some() {
bail!(
"Max fee raw and fee estimate multiplier are not supported for STRK \
transactions. Use `--fee eth` instead."
);
}
}
};

Ok(Self {
wait: value.wait || value.walnut,
receipt: value.receipt,
max_fee_raw: value.max_fee_raw,
fee_config: match value.fee {
FeeToken::Strk => {
FeeConfig::Strk(StrkFeeConfig { gas: value.gas, gas_price: value.gas_price })
}
FeeToken::Eth => FeeConfig::Eth(EthFeeConfig {
max_fee_raw: value.max_fee_raw,
fee_estimate_multiplier: value.fee_estimate_multiplier,
}),
},
walnut: value.walnut,
})
}
}

#[derive(Debug, Default, Clone)]
pub enum FeeToken {
#[default]
Strk,
Eth,
}

impl ValueEnum for FeeToken {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Eth, Self::Strk]
}

fn to_possible_value(&self) -> Option<PossibleValue> {
match self {
Self::Eth => Some(PossibleValue::new("ETH").alias("eth")),
Self::Strk => Some(PossibleValue::new("STRK").alias("strk")),
}
}
}

impl Display for FeeToken {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Eth => write!(f, "ETH"),
Self::Strk => write!(f, "STRK"),
}
}
}

#[cfg(test)]
mod tests {
use anyhow::Result;

use super::*;

#[test]
fn test_strk_conversion() -> Result<()> {
let opts = TransactionOptions {
wait: true,
receipt: true,
fee: FeeToken::Strk,
gas: Some(1000),
gas_price: Some(100),
max_fee_raw: None,
fee_estimate_multiplier: None,
walnut: false,
};

let config: TxnConfig = opts.try_into()?;

assert!(config.wait);
assert!(config.receipt);
assert!(!config.walnut);

match config.fee_config {
FeeConfig::Strk(strk_config) => {
assert_eq!(strk_config.gas, Some(1000));
assert_eq!(strk_config.gas_price, Some(100));
}
_ => panic!("Expected STRK fee config"),
}

Ok(())
}

#[test]
fn test_eth_conversion() -> Result<()> {
let opts = TransactionOptions {
wait: false,
receipt: true,
fee: FeeToken::Eth,
gas: None,
gas_price: None,
max_fee_raw: Some(Felt::from(1000)),
fee_estimate_multiplier: Some(1.5),
walnut: true,
};

let config: TxnConfig = opts.try_into()?;

assert!(config.wait);
assert!(config.receipt);
assert!(config.walnut);

match config.fee_config {
FeeConfig::Eth(eth_config) => {
assert_eq!(eth_config.max_fee_raw, Some(Felt::from(1000)));
assert_eq!(eth_config.fee_estimate_multiplier, Some(1.5));
}
_ => panic!("Expected ETH fee config"),
}

Ok(())
}

#[test]
fn test_invalid_strk_config() {
let opts = TransactionOptions {
fee: FeeToken::Strk,
max_fee_raw: Some(Felt::from(1000)),
fee_estimate_multiplier: Some(1.5),
..Default::default()
};

let result: Result<TxnConfig, _> = opts.try_into();
assert!(result.is_err());
}

#[test]
fn test_invalid_eth_config() {
let opts = TransactionOptions {
fee: FeeToken::Eth,
gas: Some(1000),
gas_price: Some(100),
..Default::default()
};
let result: Result<TxnConfig, _> = opts.try_into();
assert!(result.is_err());
}

#[test]
fn test_fee_token_display() {
assert_eq!(FeeToken::Eth.to_string(), "ETH");
assert_eq!(FeeToken::Strk.to_string(), "STRK");
}
}
5 changes: 1 addition & 4 deletions crates/dojo/utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ pub use tx::deployer::*;
pub use tx::error::TransactionError;
pub use tx::invoker::*;
pub use tx::waiter::*;
pub use tx::{
get_predeployed_accounts, parse_block_id, TransactionExt, TransactionResult, TxnAction,
TxnConfig,
};
pub use tx::*;

pub mod env;
pub mod keystore;
Expand Down
21 changes: 18 additions & 3 deletions crates/dojo/utils/src/tx/declarer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use starknet::core::types::{
};
use starknet::providers::{Provider, ProviderError};

use crate::{TransactionError, TransactionExt, TransactionResult, TransactionWaiter, TxnConfig};
use crate::{
FeeConfig, TransactionError, TransactionExt, TransactionResult, TransactionWaiter, TxnConfig,
};

/// A declarer is in charge of declaring contracts.
#[derive(Debug)]
Expand Down Expand Up @@ -92,8 +94,21 @@ where
Err(e) => return Err(TransactionError::Provider(e)),
}

let DeclareTransactionResult { transaction_hash, class_hash } =
account.declare_v2(Arc::new(class), casm_class_hash).send_with_cfg(txn_config).await?;
let DeclareTransactionResult { transaction_hash, class_hash } = match txn_config.fee_config
{
FeeConfig::Strk(_) => {
account
.declare_v3(Arc::new(class), casm_class_hash)
.send_with_cfg(txn_config)
.await?
}
FeeConfig::Eth(_) => {
account
.declare_v2(Arc::new(class), casm_class_hash)
.send_with_cfg(txn_config)
.await?
}
};

tracing::trace!(
transaction_hash = format!("{:#066x}", transaction_hash),
Expand Down
22 changes: 14 additions & 8 deletions crates/dojo/utils/src/tx/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use starknet::macros::{felt, selector};
use starknet::providers::{Provider, ProviderError};
use tracing::trace;

use crate::{TransactionError, TransactionExt, TransactionResult, TransactionWaiter, TxnConfig};
use crate::{
FeeConfig, TransactionError, TransactionExt, TransactionResult, TransactionWaiter, TxnConfig,
};

const UDC_DEPLOY_SELECTOR: Felt = selector!("deployContract");
const UDC_ADDRESS: Felt =
Expand Down Expand Up @@ -56,14 +58,18 @@ where
return Ok(TransactionResult::Noop);
}

let txn = self.account.execute_v1(vec![Call {
calldata: udc_calldata,
selector: UDC_DEPLOY_SELECTOR,
to: UDC_ADDRESS,
}]);
let call = Call { calldata: udc_calldata, selector: UDC_DEPLOY_SELECTOR, to: UDC_ADDRESS };

let InvokeTransactionResult { transaction_hash } =
txn.send_with_cfg(&self.txn_config).await?;
let InvokeTransactionResult { transaction_hash } = match self.txn_config.fee_config {
FeeConfig::Strk(_) => {
trace!("Deploying with STRK.");
self.account.execute_v3(vec![call]).send_with_cfg(&self.txn_config).await?
}
FeeConfig::Eth(_) => {
trace!("Deploying with ETH.");
self.account.execute_v1(vec![call]).send_with_cfg(&self.txn_config).await?
}
};

trace!(
transaction_hash = format!("{:#066x}", transaction_hash),
Expand Down
5 changes: 5 additions & 0 deletions crates/dojo/utils/src/tx/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ where
Provider(ProviderError),
#[error("{0}")]
TransactionExecution(String),
#[error("{0}")]
TransactionValidation(String),
#[error(transparent)]
TransactionWaiting(#[from] TransactionWaitingError),
#[error(transparent)]
Expand Down Expand Up @@ -51,6 +53,9 @@ where
ProviderError::StarknetError(StarknetError::TransactionExecutionError(te)) => {
TransactionError::TransactionExecution(te.execution_error.clone())
}
ProviderError::StarknetError(StarknetError::ValidationFailure(ve)) => {
TransactionError::TransactionExecution(ve.to_string())
}
_ => TransactionError::Provider(value),
}
}
Expand Down
Loading

0 comments on commit 3db62fa

Please sign in to comment.