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

feat: add fee token to TransactionOption and TxnConfig #2650

Merged
merged 5 commits into from
Nov 7, 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
2 changes: 1 addition & 1 deletion bin/sozo/src/commands/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,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 {
// We could save the world diff computation extracting the account directly from the
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;
glihm marked this conversation as resolved.
Show resolved Hide resolved

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),
glihm marked this conversation as resolved.
Show resolved Hide resolved
#[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
Loading