diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index a7da5dc1f9d45..d48bfd66f59e2 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -346,7 +346,10 @@ pub struct Config { /// the initial balance of each deployed test contract pub initial_balance: U256, /// the block.number value during EVM execution - #[serde(deserialize_with = "crate::deser_u64_to_u256")] + #[serde( + deserialize_with = "crate::deserialize_u64_to_u256", + serialize_with = "crate::serialize_u64_or_u256" + )] pub block_number: U256, /// pins the block number for the state fork pub fork_block_number: Option, @@ -367,7 +370,10 @@ pub struct Config { /// The `block.coinbase` value during EVM execution. pub block_coinbase: Address, /// The `block.timestamp` value during EVM execution. - #[serde(deserialize_with = "crate::deser_u64_to_u256")] + #[serde( + deserialize_with = "crate::deserialize_u64_to_u256", + serialize_with = "crate::serialize_u64_or_u256" + )] pub block_timestamp: U256, /// The `block.difficulty` value during EVM execution. pub block_difficulty: u64, @@ -3737,8 +3743,8 @@ mod tests { block_coinbase = '0x0000000000000000000000000000000000000000' block_difficulty = 0 block_prevrandao = '0x0000000000000000000000000000000000000000000000000000000000000000' - block_number = "0x1" - block_timestamp = "0x1" + block_number = 1 + block_timestamp = 1 use_literal_content = false bytecode_hash = 'ipfs' cbor_metadata = true diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index 6eeb3e3f67f93..8b316c0da9ca0 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -8,7 +8,7 @@ use foundry_compilers::artifacts::{ remappings::{Remapping, RemappingError}, }; use revm::primitives::hardfork::SpecId; -use serde::{Deserialize, Deserializer, de::Error}; +use serde::{Deserialize, Deserializer, Serializer, de::Error}; use std::{ io, path::{Path, PathBuf}, @@ -214,7 +214,7 @@ where } /// Deserialize into `U256` from either a `u64` or a `U256` hex string. -pub fn deser_u64_to_u256<'de, D>(deserializer: D) -> Result +pub fn deserialize_u64_to_u256<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { @@ -231,6 +231,26 @@ where } } +/// Serialize `U256` as `u64` if it fits, otherwise as a hex string. +/// If the number fits into a i64, serialize it as number without quotation marks. +/// If the number fits into a u64, serialize it as a stringified number with quotation marks. +/// Otherwise, serialize it as a hex string with quotation marks. +pub fn serialize_u64_or_u256(n: &U256, serializer: S) -> Result +where + S: Serializer, +{ + // The TOML specification handles integers as i64 so the number representation is limited to + // i64. If the number is larger than `i64::MAX` and up to `u64::MAX`, we serialize it as a + // string to avoid losing precision. + if let Ok(n_i64) = i64::try_from(*n) { + serializer.serialize_i64(n_i64) + } else if let Ok(n_u64) = u64::try_from(*n) { + serializer.serialize_str(&n_u64.to_string()) + } else { + serializer.serialize_str(&format!("{n:#x}")) + } +} + /// Helper type to parse both `u64` and `U256` #[derive(Clone, Copy, Deserialize)] #[serde(untagged)] diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index d86a694cb4560..5cf7321647acb 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -278,11 +278,17 @@ pub struct Env { pub block_coinbase: Address, /// the block.timestamp value during EVM execution - #[serde(deserialize_with = "foundry_config::deser_u64_to_u256")] + #[serde( + deserialize_with = "foundry_config::deserialize_u64_to_u256", + serialize_with = "foundry_config::serialize_u64_or_u256" + )] pub block_timestamp: U256, /// the block.number value during EVM execution" - #[serde(deserialize_with = "foundry_config::deser_u64_to_u256")] + #[serde( + deserialize_with = "foundry_config::deserialize_u64_to_u256", + serialize_with = "foundry_config::serialize_u64_or_u256" + )] pub block_number: U256, /// the block.difficulty value during EVM execution diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index f2f737914fa0e..bcfaca725678e 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -1016,11 +1016,11 @@ prompt_timeout = 120 sender = "0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38" tx_origin = "0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38" initial_balance = "0xffffffffffffffffffffffff" -block_number = "0x1" +block_number = 1 gas_limit = 1073741824 block_base_fee_per_gas = 0 block_coinbase = "0x0000000000000000000000000000000000000000" -block_timestamp = "0x1" +block_timestamp = 1 block_difficulty = 0 block_prevrandao = "0x0000000000000000000000000000000000000000000000000000000000000000" memory_limit = 134217728 @@ -1242,7 +1242,7 @@ exclude = [] "sender": "0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38", "tx_origin": "0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38", "initial_balance": "0xffffffffffffffffffffffff", - "block_number": "0x1", + "block_number": 1, "fork_block_number": null, "chain_id": null, "gas_limit": 1073741824, @@ -1250,7 +1250,7 @@ exclude = [] "gas_price": null, "block_base_fee_per_gas": 0, "block_coinbase": "0x0000000000000000000000000000000000000000", - "block_timestamp": "0x1", + "block_timestamp": 1, "block_difficulty": 0, "block_prevrandao": "0x0000000000000000000000000000000000000000000000000000000000000000", "block_gas_limit": null,