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

Validate max gas is greater than zero for strk fee settings #2796

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e843e47
Validate max gas is greater than zero for in for strk fee settings
franciszekjob Dec 18, 2024
ea50757
Merge branch 'master' into franciszekjob/2706-validate-fee-args-great…
franciszekjob Dec 18, 2024
8dce237
Check if max gas unit price > 0 when it's passed with max fee
franciszekjob Dec 18, 2024
72f2fe8
Merge branch 'franciszekjob/2706-validate-fee-args-greater-than-zero'…
franciszekjob Dec 18, 2024
cb3d529
Refactor checking if feee args are greater than zero
franciszekjob Dec 18, 2024
a411a4e
Update error messages
franciszekjob Dec 18, 2024
397a8ba
Update docs and changelog
franciszekjob Dec 19, 2024
77f4d4d
Update changelog
franciszekjob Dec 19, 2024
e5c22d2
Refactor checking if calculated value is zero
franciszekjob Dec 19, 2024
969e37b
Cover case when max fee and max gas unit price are passed
franciszekjob Dec 19, 2024
4788a9b
Refactor `ScriptFeeSettings` and `FeeSettings`
franciszekjob Dec 19, 2024
e23e54e
Merge branch 'master' into franciszekjob/2706-validate-fee-args-great…
franciszekjob Dec 19, 2024
31168e4
Format
franciszekjob Dec 20, 2024
69af0af
Merge branch 'franciszekjob/2706-validate-fee-args-greater-than-zero'…
franciszekjob Dec 20, 2024
7b7337e
Rename `zero` to `0`
franciszekjob Dec 20, 2024
2db18e6
Update changelog
franciszekjob Dec 20, 2024
41e4cd5
Add `impl_deserialize_for_nonzero_num_type` macro
franciszekjob Dec 20, 2024
73e085f
Apply other code review suggestions
franciszekjob Dec 20, 2024
7d4bbed
Update changelog
franciszekjob Jan 7, 2025
0779442
Add conversions tests
franciszekjob Jan 7, 2025
45cf019
Merge branch 'master' of https://github.com/foundry-rs/starknet-found…
franciszekjob Jan 7, 2025
0c80350
Update changelog - remove duplicated entry
franciszekjob Jan 7, 2025
0e185b6
Fix headings in changelog
franciszekjob Jan 7, 2025
a784e69
Refactor `FeeArgs.try_into_fee_settings()`
franciszekjob Jan 7, 2025
091e484
Update `unreachable!` messages in `FeeArgs.try_into_fee_settings()`
franciszekjob Jan 7, 2025
9682eab
Use `Felt::TWO.pow`
franciszekjob Jan 10, 2025
a7c0acf
Remove use of `to_bigint()` in `impl_deserialize_for_num_type`
franciszekjob Jan 10, 2025
2eb48ae
Merge branch 'master' of https://github.com/foundry-rs/starknet-found…
franciszekjob Jan 10, 2025
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Cast

### Changed

- Values passed to the `--max-fee`, `--max-gas`, and `--max-gas-unit-price` flags must be greater than 0
ddoktorski marked this conversation as resolved.
Show resolved Hide resolved

## [0.35.1] - 2024-12-16

### Forge
Expand Down
71 changes: 41 additions & 30 deletions crates/sncast/src/helpers/fee.rs
cptartur marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ pub struct FeeArgs {
pub fee_token: Option<FeeToken>,

/// Max fee for the transaction. If not provided, will be automatically estimated.
#[clap(short, long)]
pub max_fee: Option<Felt>,
#[clap(value_parser = parse_non_zero_felt, short, long)]
pub max_fee: Option<NonZeroFelt>,

/// Max gas amount. If not provided, will be automatically estimated. (Only for STRK fee payment)
#[clap(long)]
pub max_gas: Option<Felt>,
#[clap(value_parser = parse_non_zero_felt, long)]
pub max_gas: Option<NonZeroFelt>,

/// Max gas price in Fri. If not provided, will be automatically estimated. (Only for STRK fee payment)
#[clap(long)]
pub max_gas_unit_price: Option<Felt>,
#[clap(value_parser = parse_non_zero_felt, long)]
pub max_gas_unit_price: Option<NonZeroFelt>,
}

impl From<ScriptFeeSettings> for FeeArgs {
franciszekjob marked this conversation as resolved.
Show resolved Hide resolved
fn from(script_fee_settings: ScriptFeeSettings) -> Self {
match script_fee_settings {
ScriptFeeSettings::Eth { max_fee } => Self {
fee_token: Some(FeeToken::Eth),
max_fee,
max_fee: max_fee.and_then(|value| NonZeroFelt::try_from(value).ok()),
max_gas: None,
max_gas_unit_price: None,
},
Expand All @@ -42,9 +42,13 @@ impl From<ScriptFeeSettings> for FeeArgs {
max_gas_unit_price,
} => Self {
fee_token: Some(FeeToken::Strk),
max_fee,
max_gas: max_gas.map(Into::into),
max_gas_unit_price: max_gas_unit_price.map(Into::into),
max_fee: max_fee.and_then(|value| NonZeroFelt::try_from(value).ok()),
max_gas: max_gas
.map(Into::into)
.and_then(|val: Felt| NonZeroFelt::try_from(val).ok()),
max_gas_unit_price: max_gas_unit_price
.map(Into::into)
.and_then(|val: Felt| NonZeroFelt::try_from(val).ok()),
},
}
}
Expand Down Expand Up @@ -75,7 +79,7 @@ impl FeeArgs {
"--max-gas-unit-price is not supported for ETH fee payment"
);
Ok(FeeSettings::Eth {
max_fee: self.max_fee,
max_fee: self.max_fee.map(Felt::from),
})
}
FeeToken::Strk => {
Expand All @@ -92,43 +96,44 @@ impl FeeArgs {
bail!("--max-fee should be greater than or equal to --max-gas-unit-price")
}
(None, _, _) => FeeSettings::Strk {
max_gas: self.max_gas.map(TryIntoConv::try_into_).transpose()?,
max_gas: self
.max_gas
.map(Felt::from)
.map(TryIntoConv::try_into_)
.transpose()?,
max_gas_unit_price: self
.max_gas_unit_price
.map(Felt::from)
.map(TryIntoConv::try_into_)
.transpose()?,
},
(Some(max_fee), None, Some(max_gas_unit_price)) => FeeSettings::Strk {
max_gas: Some(
max_fee
.floor_div(&NonZeroFelt::from_felt_unchecked(max_gas_unit_price))
.try_into_()?,
),
max_gas_unit_price: Some(max_gas_unit_price.try_into_()?),
},
(Some(max_fee), Some(max_gas), None) => FeeSettings::Strk {
max_gas: Some(max_gas.try_into_()?),
max_gas_unit_price: Some(
max_fee
.floor_div(&NonZeroFelt::from_felt_unchecked(max_gas))
Felt::from(max_fee)
.floor_div(&max_gas_unit_price)
.try_into_()?,
),
max_gas_unit_price: Some(Felt::from(max_gas_unit_price).try_into()?),
},
(Some(max_fee), Some(max_gas), None) => {
let max_gas_unit_price = NonZeroFelt::try_from(Felt::from(max_fee).floor_div(&max_gas)).expect("Calculated max gas unit price from provided --max-fee and --max-gas is zero. Please increase --max-fee or decrease --max-gas to ensure a positive gas unit price");
franciszekjob marked this conversation as resolved.
Show resolved Hide resolved

FeeSettings::Strk {
max_gas: Some(Felt::from(max_gas).try_into_()?),
max_gas_unit_price: Some(Felt::from(max_gas_unit_price).try_into_()?),
}
}
(Some(max_fee), None, None) => {
let max_gas_unit_price = provider
.get_block_with_tx_hashes(block_id)
.await?
.l1_gas_price()
.price_in_fri;
let max_gas = NonZeroFelt::try_from(Felt::from(max_fee)
.floor_div(&NonZeroFelt::try_from(max_gas_unit_price)?)).expect("Calculated max-gas from provided --max-fee and the current network gas price is zero. Please increase --max-fee to obtain a positive gas amount");
franciszekjob marked this conversation as resolved.
Show resolved Hide resolved

FeeSettings::Strk {
max_gas: Some(
max_fee
.floor_div(&NonZeroFelt::from_felt_unchecked(
max_gas_unit_price,
))
.try_into_()?,
),
max_gas: Some(Felt::from(max_gas).try_into_()?),
max_gas_unit_price: Some(max_gas_unit_price.try_into_()?),
}
}
Expand Down Expand Up @@ -261,3 +266,9 @@ fn parse_fee_token(s: &str) -> Result<FeeToken, String> {

Ok(parsed_token)
}

fn parse_non_zero_felt(s: &str) -> Result<NonZeroFelt, String> {
let felt: Felt = s.parse().map_err(|_| "Failed to parse value")?;
felt.try_into()
.map_err(|_| "Value should be greater than 0".to_string())
}
70 changes: 70 additions & 0 deletions crates/sncast/tests/e2e/invoke.rs
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these tests refer to the reported issue?
#2697
I suppose that even if max_fee is greater than 0 an error may occur

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These ones refer to situation when user explicitly passes 0 to fee args.
The case when max gas or max gas unit price is calculated based on other values, is handled e.g. here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most important cases to test here are the ones where values calculated from --max-fee are 0. We should add these.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine, so let's break it down:

Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,76 @@ fn test_too_low_max_fee() {
);
}

#[test]
fn test_max_gas_equal_to_zero() {
let args = vec![
"--accounts-file",
ACCOUNT_FILE_PATH,
"--account",
"user11",
"--wait",
"invoke",
"--url",
URL,
"--contract-address",
MAP_CONTRACT_ADDRESS_SEPOLIA,
"--function",
"put",
"--calldata",
"0x1",
"0x2",
"--max-gas",
"0",
"--fee-token",
"strk",
];

let snapbox = runner(&args);
let output = snapbox.assert().code(2);

assert_stderr_contains(
output,
indoc! {r"
error: invalid value '0' for '--max-gas <MAX_GAS>': Value should be greater than 0
"},
);
}

#[test]
fn test_max_gas_calculated_from_max_fee_equal_to_zero() {
let args = vec![
"--accounts-file",
ACCOUNT_FILE_PATH,
"--account",
"user11",
"--wait",
"invoke",
"--url",
URL,
"--contract-address",
MAP_CONTRACT_ADDRESS_SEPOLIA,
"--function",
"put",
"--calldata",
"0x1",
"0x2",
"--max-fee",
"0",
"--fee-token",
"strk",
];

let snapbox = runner(&args);
let output = snapbox.assert().code(2);

assert_stderr_contains(
output,
indoc! {r"
error: invalid value '0' for '--max-fee <MAX_FEE>': Value should be greater than 0
"},
);
}

#[tokio::test]
async fn test_happy_case_cairo_expression_calldata() {
let tempdir = create_and_deploy_oz_account().await;
Expand Down
40 changes: 20 additions & 20 deletions crates/sncast/tests/integration/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use starknet::accounts::{AccountFactory, OpenZeppelinAccountFactory};
use starknet::providers::jsonrpc::HttpTransport;
use starknet::providers::{JsonRpcClient, Provider};
use starknet::signers::{LocalWallet, SigningKey};
use starknet_types_core::felt::Felt;
use url::Url;

const MAX_FEE: u64 = 1_000_000_000_000;

async fn get_factory() -> OpenZeppelinAccountFactory<LocalWallet, JsonRpcClient<HttpTransport>> {
Expand All @@ -26,7 +26,7 @@ async fn test_happy_case_eth() {

let args = FeeArgs {
fee_token: Some(FeeToken::Eth),
max_fee: Some(100_u32.into()),
max_fee: Some(Felt::from(100_u32).try_into().unwrap()),
max_gas: None,
max_gas_unit_price: None,
};
Expand All @@ -50,8 +50,8 @@ async fn test_max_gas_eth() {

let args = FeeArgs {
fee_token: Some(FeeToken::Eth),
max_fee: Some(100_u32.into()),
max_gas: Some(100_u32.into()),
max_fee: Some(Felt::from(100_u32).try_into().unwrap()),
max_gas: Some(Felt::from(100_u32).try_into().unwrap()),
max_gas_unit_price: None,
};

Expand All @@ -71,9 +71,9 @@ async fn test_max_gas_unit_price_eth() {

let args = FeeArgs {
fee_token: Some(FeeToken::Eth),
max_fee: Some(100_u32.into()),
max_fee: Some(Felt::from(100).try_into().unwrap()),
max_gas: None,
max_gas_unit_price: Some(100_u32.into()),
max_gas_unit_price: Some(Felt::from(100_u32).try_into().unwrap()),
};

let error = args
Expand All @@ -92,9 +92,9 @@ async fn test_all_args() {

let args = FeeArgs {
fee_token: Some(FeeToken::Strk),
max_fee: Some(100_u32.into()),
max_gas: Some(100_u32.into()),
max_gas_unit_price: Some(100_u32.into()),
max_fee: Some(Felt::from(100_u32).try_into().unwrap()),
max_gas: Some(Felt::from(100_u32).try_into().unwrap()),
max_gas_unit_price: Some(Felt::from(100_u32).try_into().unwrap()),
};

let error = args
Expand All @@ -113,8 +113,8 @@ async fn test_max_fee_less_than_max_gas() {

let args = FeeArgs {
fee_token: Some(FeeToken::Strk),
max_fee: Some(50_u32.into()),
max_gas: Some(100_u32.into()),
max_fee: Some(Felt::from(50_u32).try_into().unwrap()),
max_gas: Some(Felt::from(100_u32).try_into().unwrap()),
max_gas_unit_price: None,
};

Expand All @@ -134,9 +134,9 @@ async fn test_max_fee_less_than_max_gas_unit_price() {

let args = FeeArgs {
fee_token: Some(FeeToken::Strk),
max_fee: Some(50_u32.into()),
max_fee: Some(Felt::from(50_u32).try_into().unwrap()),
max_gas: None,
max_gas_unit_price: Some(100_u32.into()),
max_gas_unit_price: Some(Felt::from(100).try_into().unwrap()),
};

let error = args
Expand All @@ -154,7 +154,7 @@ async fn test_strk_fee_get_max_fee() {

let args = FeeArgs {
fee_token: Some(FeeToken::Strk),
max_fee: Some(MAX_FEE.into()),
max_fee: Some(Felt::from(MAX_FEE).try_into().unwrap()),
max_gas: None,
max_gas_unit_price: None,
};
Expand Down Expand Up @@ -184,8 +184,8 @@ async fn test_strk_fee_get_max_fee_with_max_gas() {

let args = FeeArgs {
fee_token: Some(FeeToken::Strk),
max_fee: Some(MAX_FEE.into()),
max_gas: Some(1_000_000_u32.into()),
max_fee: Some(Felt::from(MAX_FEE).try_into().unwrap()),
max_gas: Some(Felt::from(1_000_000_u32).try_into().unwrap()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you using NonZeroU64::new in some test cases and Felt::from... in other cases for the same field? Let's pick a consistent behavior if it's possible.

Copy link
Collaborator Author

@franciszekjob franciszekjob Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because fee-related fields in FeeArgs use NonZeroFelt while in FeeSettings::Strk/Eth they use either NonZeroFelt, NonZeroU64 or NonZeroU128 (ofc wrapped Option).

max_gas_unit_price: None,
};

Expand Down Expand Up @@ -223,8 +223,8 @@ async fn test_strk_fee_get_max_gas_and_max_gas_unit_price() {
let args = FeeArgs {
fee_token: Some(FeeToken::Strk),
max_fee: None,
max_gas: Some(1_000_000_u32.into()),
max_gas_unit_price: Some(1_000_u32.into()),
max_gas: Some(Felt::from(1_000_000_u32).try_into().unwrap()),
max_gas_unit_price: Some(Felt::from(1_000_u32).try_into().unwrap()),
};

let settings = args
Expand All @@ -247,9 +247,9 @@ async fn test_strk_fee_get_max_fee_with_max_gas_unit_price() {

let args = FeeArgs {
fee_token: Some(FeeToken::Strk),
max_fee: Some(MAX_FEE.into()),
max_fee: Some(Felt::from(MAX_FEE).try_into().unwrap()),
max_gas: None,
max_gas_unit_price: Some(1_000_u32.into()),
max_gas_unit_price: Some(Felt::from(1_000_u32).try_into().unwrap()),
};

let settings = args
Expand Down
6 changes: 3 additions & 3 deletions docs/src/appendix/sncast/account/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Overrides url from `snfoundry.toml`.
## `--max-fee, -m <MAX_FEE>`
Optional.

Maximum fee for the `deploy_account` transaction in Fri or Wei depending on fee token or transaction version. When not used, defaults to auto-estimation.
Maximum fee for the `deploy_account` transaction in Fri or Wei depending on fee token or transaction version. When not used, defaults to auto-estimation. Must be greater than zero.

## `--fee-token <FEE_TOKEN>`
Optional. When not used, defaults to STRK.
Expand All @@ -26,12 +26,12 @@ Token used for fee payment. Possible values: ETH, STRK.
## `--max-gas <MAX_GAS>`
Optional.

Maximum gas for the `deploy_account` transaction. When not used, defaults to auto-estimation. (Only for STRK fee payment)
Maximum gas for the `deploy_account` transaction. When not used, defaults to auto-estimation. Must be greater than zero. (Only for STRK fee payment)

## ` --max-gas-unit-price <MAX_GAS_UNIT_PRICE>`
Optional.

Maximum gas unit price for the `deploy_account` transaction paid in Fri. When not used, defaults to auto-estimation. (Only for STRK fee payment)
Maximum gas unit price for the `deploy_account` transaction paid in Fri. When not used, defaults to auto-estimation. Must be greater than zero. (Only for STRK fee payment)

## `--version, -v <VERSION>`
Optional. When not used, defaults to v3.
Expand Down
6 changes: 3 additions & 3 deletions docs/src/appendix/sncast/declare.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Overrides url from `snfoundry.toml`.
## `--max-fee, -m <MAX_FEE>`
Optional.

Maximum fee for the `declare` transaction in Fri or Wei depending on fee token or transaction version. When not used, defaults to auto-estimation.
Maximum fee for the `declare` transaction in Fri or Wei depending on fee token or transaction version. When not used, defaults to auto-estimation. Must be greater than zero.

## `--fee-token <FEE_TOKEN>`
Optional. When not used, defaults to STRK.
Expand All @@ -30,12 +30,12 @@ Token used for fee payment. Possible values: ETH, STRK.
## `--max-gas <MAX_GAS>`
Optional.

Maximum gas for the `declare` transaction. When not used, defaults to auto-estimation. (Only for STRK fee payment)
Maximum gas for the `declare` transaction. When not used, defaults to auto-estimation. Must be greater than zero. (Only for STRK fee payment)

## ` --max-gas-unit-price <MAX_GAS_UNIT_PRICE>`
Optional.

Maximum gas unit price for the `declare` transaction paid in Fri. When not used, defaults to auto-estimation. (Only for STRK fee payment)
Maximum gas unit price for the `declare` transaction paid in Fri. When not used, defaults to auto-estimation. Must be greater than zero. (Only for STRK fee payment)

## `--version, -v <VERSION>`
Optional. When not used, defaults to v3.
Expand Down
Loading
Loading