Skip to content

Commit

Permalink
Merge branch 'master' into expectCreate
Browse files Browse the repository at this point in the history
  • Loading branch information
grandizzy authored Feb 20, 2025
2 parents 1fde43e + 1091634 commit 99b01ea
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 12 deletions.
4 changes: 3 additions & 1 deletion crates/anvil/src/eth/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ impl From<revm::primitives::InvalidTransaction> for InvalidTransactionError {
InvalidTransaction::CallGasCostMoreThanGasLimit => {
Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
}
InvalidTransaction::GasFloorMoreThanGasLimit => {
Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") })
}
InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA,
InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds,
InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow,
Expand All @@ -305,7 +308,6 @@ impl From<revm::primitives::InvalidTransaction> for InvalidTransactionError {
InvalidTransaction::OptimismError(_) |
InvalidTransaction::EofCrateShouldHaveToAddress |
InvalidTransaction::EmptyAuthorizationList => Self::Revm(err),
InvalidTransaction::GasFloorMoreThanGasLimit => Self::Revm(err),
}
}
}
Expand Down
17 changes: 16 additions & 1 deletion crates/anvil/tests/it/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
utils::{connect_pubsub, http_provider_with_signer},
};
use alloy_network::{EthereumWallet, TransactionBuilder, TransactionResponse};
use alloy_primitives::{map::B256HashSet, Address, Bytes, FixedBytes, U256};
use alloy_primitives::{address, hex, map::B256HashSet, Address, Bytes, FixedBytes, U256};
use alloy_provider::Provider;
use alloy_rpc_types::{
state::{AccountOverride, StateOverride},
Expand Down Expand Up @@ -1235,3 +1235,18 @@ async fn can_mine_multiple_in_block() {
let txs = block.transactions.hashes().collect::<Vec<_>>();
assert_eq!(txs, vec![first, second]);
}

// ensures that the gas estimate is running on pending block by default
#[tokio::test(flavor = "multi_thread")]
async fn estimates_gas_prague() {
let (api, _handle) =
spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into()))).await;

// {"data":"0xcafebabe","from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"
// 0x70997970c51812dc3a010c7d01b50e0d17dc79c8"}
let req = TransactionRequest::default()
.with_input(hex!("0xcafebabe"))
.with_from(address!("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"))
.with_to(address!("0x70997970c51812dc3a010c7d01b50e0d17dc79c8"));
api.estimate_gas(WithOtherFields::new(req), None, None).await.unwrap();
}
2 changes: 1 addition & 1 deletion crates/cast/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ async fn main_args(args: CastArgs) -> Result<()> {
let config = rpc.load_config()?;
let provider = utils::get_provider(&config)?;
sh_println!(
"{}",
"{} UTC",
Cast::new(provider).age(block.unwrap_or(BlockId::Number(Latest))).await?
)?
}
Expand Down
40 changes: 40 additions & 0 deletions crates/cheatcodes/assets/cheatcodes.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions crates/cheatcodes/spec/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,27 @@ interface Vm {
string calldata error
) external pure;

/// Returns true if the current Foundry version is greater than or equal to the given version.
/// The given version string must be in the format `major.minor.patch`.
///
/// This is equivalent to `foundryVersionCmp(version) >= 0`.
#[cheatcode(group = Testing, safety = Safe)]
function foundryVersionAtLeast(string calldata version) external view returns (bool);

/// Compares the current Foundry version with the given version string.
/// The given version string must be in the format `major.minor.patch`.
///
/// Returns:
/// -1 if current Foundry version is less than the given version
/// 0 if current Foundry version equals the given version
/// 1 if current Foundry version is greater than the given version
///
/// This result can then be used with a comparison operator against `0`.
/// For example, to check if the current Foundry version is greater than or equal to `1.0.0`:
/// `if (foundryVersionCmp("1.0.0") >= 0) { ... }`
#[cheatcode(group = Testing, safety = Safe)]
function foundryVersionCmp(string calldata version) external view returns (int256);

// ======== OS and Filesystem ========

// -------- Metadata --------
Expand Down
2 changes: 2 additions & 0 deletions crates/cheatcodes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ mod config;

mod crypto;

mod version;

mod env;
pub use env::set_execution_context;

Expand Down
41 changes: 41 additions & 0 deletions crates/cheatcodes/src/version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::{Cheatcode, Cheatcodes, Result, Vm::*};
use alloy_sol_types::SolValue;
use foundry_common::version::SEMVER_VERSION;
use semver::Version;
use std::cmp::Ordering;

impl Cheatcode for foundryVersionCmpCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { version } = self;
foundry_version_cmp(version).map(|cmp| (cmp as i8).abi_encode())
}
}

impl Cheatcode for foundryVersionAtLeastCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { version } = self;
foundry_version_cmp(version).map(|cmp| cmp.is_ge().abi_encode())
}
}

fn foundry_version_cmp(version: &str) -> Result<Ordering> {
version_cmp(SEMVER_VERSION.split('-').next().unwrap(), version)
}

fn version_cmp(version_a: &str, version_b: &str) -> Result<Ordering> {
let version_a = parse_version(version_a)?;
let version_b = parse_version(version_b)?;
Ok(version_a.cmp(&version_b))
}

fn parse_version(version: &str) -> Result<Version> {
let version =
Version::parse(version).map_err(|e| fmt_err!("invalid version `{version}`: {e}"))?;
if !version.pre.is_empty() {
return Err(fmt_err!("invalid version `{version}`: pre-release versions are not supported"));
}
if !version.build.is_empty() {
return Err(fmt_err!("invalid version `{version}`: build metadata is not supported"));
}
Ok(version)
}
2 changes: 2 additions & 0 deletions crates/forge/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,8 @@ pub struct TestSetup {
pub reason: Option<String>,
/// Whether setup and entire test suite is skipped.
pub skipped: bool,
/// Whether the test failed to deploy.
pub deployment_failure: bool,
}

impl TestSetup {
Expand Down
10 changes: 9 additions & 1 deletion crates/forge/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ impl<'a> ContractRunner<'a> {
U256::ZERO,
Some(&self.mcr.revert_decoder),
);

result.deployment_failure = deploy_result.is_err();

if let Ok(dr) = &deploy_result {
debug_assert_eq!(dr.address, address);
}
Expand Down Expand Up @@ -340,9 +343,14 @@ impl<'a> ContractRunner<'a> {

if setup.reason.is_some() {
// The setup failed, so we return a single test result for `setUp`
let fail_msg = if !setup.deployment_failure {
"setUp()".to_string()
} else {
"constructor()".to_string()
};
return SuiteResult::new(
start.elapsed(),
[("setUp()".to_string(), TestResult::setup_result(setup))].into(),
[(fail_msg, TestResult::setup_result(setup))].into(),
warnings,
)
}
Expand Down
31 changes: 31 additions & 0 deletions crates/forge/tests/cli/test_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3137,3 +3137,34 @@ Encountered a total of 1 failing tests, 1 tests succeeded
"#]]);
});

forgetest_init!(catch_test_deployment_failure, |prj, cmd| {
prj.add_test(
"TestDeploymentFailure.t.sol",
r#"
import "forge-std/Test.sol";
contract TestDeploymentFailure is Test {
constructor() {
require(false);
}
function setUp() public {
require(true);
}
function test_something() public {
require(1 == 1);
}
}
"#,
)
.unwrap();

cmd.args(["t", "--mt", "test_something"]).assert_failure().stdout_eq(str![[r#"
...
Failing tests:
Encountered 1 failing test in test/TestDeploymentFailure.t.sol:TestDeploymentFailure
[FAIL: EvmError: Revert] constructor() ([GAS])
..."#]]);
});
16 changes: 8 additions & 8 deletions crates/test-utils/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ static ALCHEMY_KEYS: LazyLock<Vec<&'static str>> = LazyLock::new(|| {
// "GL4M0hfzSYGU5e1_t804HoUDOObWP-FA",
// "WV407BEiBmjNJfKo9Uo_55u0z0ITyCOX",
// "Ge56dH9siMF4T0whP99sQXOcr2mFs8wZ",
// "QC55XC151AgkS3FNtWvz9VZGeu9Xd9lb",
// "pwc5rmJhrdoaSEfimoKEmsvOjKSmPDrP",
// "A5sZ85MIr4SzCMkT0zXh2eeamGIq3vGL",
// "9VWGraLx0tMiSWx05WH-ywgSVmMxs66W",
"QC55XC151AgkS3FNtWvz9VZGeu9Xd9lb",
"pwc5rmJhrdoaSEfimoKEmsvOjKSmPDrP",
"A5sZ85MIr4SzCMkT0zXh2eeamGIq3vGL",
"9VWGraLx0tMiSWx05WH-ywgSVmMxs66W",
// "U4hsGWgl9lBM1j3jhSgJ4gbjHg2jRwKy",
"K-uNlqYoYCO9cdBHcifwCDAcEjDy1UHL",
"GWdgwabOE2XfBdLp_gIq-q6QHa7DSoag",
"Uz0cF5HCXFtpZlvd9NR7kHxfB_Wdpsx7",
"wWZMf1SOu9lT1GNIJHOX-5WL1MiYXycT",
// "K-uNlqYoYCO9cdBHcifwCDAcEjDy1UHL",
// "GWdgwabOE2XfBdLp_gIq-q6QHa7DSoag",
// "Uz0cF5HCXFtpZlvd9NR7kHxfB_Wdpsx7",
// "wWZMf1SOu9lT1GNIJHOX-5WL1MiYXycT",
// "HACxy4wNUoD-oLlCq_v5LG0bclLc_DRL",
// "_kCjfMjYo8x0rOm6YzmvSI0Qk-c8SO5I",
// "kD-M-g5TKb957S3bbOXxXPeMUxm1uTuU",
Expand Down
2 changes: 2 additions & 0 deletions testdata/cheats/Vm.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions testdata/default/cheats/GetFoundryVersion.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,49 @@ contract GetFoundryVersionTest is DSTest {
// Validate build profile (e.g., "debug" or "release")
require(bytes(buildType).length > 0, "Build type is empty");
}

function testFoundryVersionCmp() public {
// Should return -1 if current version is less than argument
assertEq(vm.foundryVersionCmp("99.0.0"), -1);

// (e.g. 0.3.0-nightly+3cb96bde9b.1737036656.debug)
string memory fullVersionString = vm.getFoundryVersion();

// Step 1: Split the version at "+"
string[] memory plusSplit = vm.split(fullVersionString, "+");
require(plusSplit.length == 2, "Invalid version format: Missing '+' separator");

// Step 2: Extract parts
string memory semanticVersion = plusSplit[0]; // "0.3.0-dev"
string[] memory semanticSplit = vm.split(semanticVersion, "-");

semanticVersion = semanticSplit[0]; // "0.3.0"
// Should return 0 if current version is equal to argument
assertEq(vm.foundryVersionCmp(semanticVersion), 0);

// Should return 1 if current version is greater than argument
assertEq(vm.foundryVersionCmp("0.0.1"), 1);
}

function testFoundryVersionAtLeast() public {
// Should return false for future versions
assertEq(vm.foundryVersionAtLeast("99.0.0"), false);

// (e.g. 0.3.0-nightly+3cb96bde9b.1737036656.debug)
string memory fullVersionString = vm.getFoundryVersion();

// Step 1: Split the version at "+"
string[] memory plusSplit = vm.split(fullVersionString, "+");
require(plusSplit.length == 2, "Invalid version format: Missing '+' separator");

// Step 2: Extract parts
string memory semanticVersion = plusSplit[0]; // "0.3.0-dev"
string[] memory semanticSplit = vm.split(semanticVersion, "-");

semanticVersion = semanticSplit[0]; // "0.3.0"
assertTrue(vm.foundryVersionAtLeast(semanticVersion));

// Should return true for past versions
assertTrue(vm.foundryVersionAtLeast("0.2.0"));
}
}

0 comments on commit 99b01ea

Please sign in to comment.