diff --git a/.gitignore b/.gitignore
index 19f666e451bf9..5b61e32022994 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 .DS_STORE
 /target
 out/
+snapshots/
 out.json
 .idea
 .vscode
diff --git a/Cargo.lock b/Cargo.lock
index cc0bdae612347..a4426217470da 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -89,28 +89,44 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1"
 dependencies = [
- "alloy-eips",
+ "alloy-eips 0.3.6",
  "alloy-primitives",
  "alloy-rlp",
- "alloy-serde",
+ "alloy-serde 0.3.6",
  "c-kzg",
  "serde",
 ]
 
+[[package]]
+name = "alloy-consensus"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e"
+dependencies = [
+ "alloy-eips 0.4.2",
+ "alloy-primitives",
+ "alloy-rlp",
+ "alloy-serde 0.4.2",
+ "auto_impl",
+ "c-kzg",
+ "derive_more 1.0.0",
+ "serde",
+]
+
 [[package]]
 name = "alloy-contract"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eefe64fd344cffa9cf9e3435ec4e93e6e9c3481bc37269af988bf497faf4a6a"
+checksum = "917f7d12cf3971dc8c11c9972f732b35ccb9aaaf5f28f2f87e9e6523bee3a8ad"
 dependencies = [
  "alloy-dyn-abi",
  "alloy-json-abi",
  "alloy-network",
- "alloy-network-primitives",
+ "alloy-network-primitives 0.4.0",
  "alloy-primitives",
  "alloy-provider",
  "alloy-pubsub",
- "alloy-rpc-types-eth",
+ "alloy-rpc-types-eth 0.4.0",
  "alloy-sol-types",
  "alloy-transport",
  "futures",
@@ -154,9 +170,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-eip7702"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a"
+checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04"
 dependencies = [
  "alloy-primitives",
  "alloy-rlp",
@@ -176,7 +192,25 @@ dependencies = [
  "alloy-eip7702",
  "alloy-primitives",
  "alloy-rlp",
- "alloy-serde",
+ "alloy-serde 0.3.6",
+ "c-kzg",
+ "derive_more 1.0.0",
+ "once_cell",
+ "serde",
+ "sha2",
+]
+
+[[package]]
+name = "alloy-eips"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85"
+dependencies = [
+ "alloy-eip2930",
+ "alloy-eip7702",
+ "alloy-primitives",
+ "alloy-rlp",
+ "alloy-serde 0.4.2",
  "c-kzg",
  "derive_more 1.0.0",
  "once_cell",
@@ -186,12 +220,12 @@ dependencies = [
 
 [[package]]
 name = "alloy-genesis"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca"
+checksum = "8429cf4554eed9b40feec7f4451113e76596086447550275e3def933faf47ce3"
 dependencies = [
  "alloy-primitives",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "serde",
 ]
 
@@ -209,9 +243,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-json-rpc"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3c717b5298fad078cd3a418335b266eba91b511383ca9bd497f742d5975d5ab"
+checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7"
 dependencies = [
  "alloy-primitives",
  "alloy-sol-types",
@@ -223,17 +257,17 @@ dependencies = [
 
 [[package]]
 name = "alloy-network"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049"
+checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd"
 dependencies = [
- "alloy-consensus",
- "alloy-eips",
+ "alloy-consensus 0.4.2",
+ "alloy-eips 0.4.2",
  "alloy-json-rpc",
- "alloy-network-primitives",
+ "alloy-network-primitives 0.4.0",
  "alloy-primitives",
- "alloy-rpc-types-eth",
- "alloy-serde",
+ "alloy-rpc-types-eth 0.4.0",
+ "alloy-serde 0.4.2",
  "alloy-signer",
  "alloy-sol-types",
  "async-trait",
@@ -248,9 +282,22 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3"
 dependencies = [
- "alloy-eips",
+ "alloy-eips 0.3.6",
+ "alloy-primitives",
+ "alloy-serde 0.3.6",
+ "serde",
+]
+
+[[package]]
+name = "alloy-network-primitives"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8416e4e9ceee8014d2f89fc3dde331da392b26d14226a0d5cbc207ae7799fb2f"
+dependencies = [
+ "alloy-consensus 0.4.2",
+ "alloy-eips 0.4.2",
  "alloy-primitives",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "serde",
 ]
 
@@ -287,20 +334,20 @@ dependencies = [
 
 [[package]]
 name = "alloy-provider"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5"
+checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6"
 dependencies = [
  "alloy-chains",
- "alloy-consensus",
- "alloy-eips",
+ "alloy-consensus 0.4.2",
+ "alloy-eips 0.4.2",
  "alloy-json-rpc",
  "alloy-network",
- "alloy-network-primitives",
+ "alloy-network-primitives 0.4.0",
  "alloy-primitives",
  "alloy-pubsub",
  "alloy-rpc-client",
- "alloy-rpc-types-eth",
+ "alloy-rpc-types-eth 0.4.0",
  "alloy-rpc-types-trace",
  "alloy-rpc-types-txpool",
  "alloy-transport",
@@ -326,9 +373,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-pubsub"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d05f63677e210d758cd5d6d1ce10f20c980c3560ccfbe79ba1997791862a04f"
+checksum = "f32cef487122ae75c91eb50154c70801d71fabdb976fec6c49e0af5e6486ab15"
 dependencies = [
  "alloy-json-rpc",
  "alloy-primitives",
@@ -367,9 +414,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-rpc-client"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5"
+checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc"
 dependencies = [
  "alloy-json-rpc",
  "alloy-primitives",
@@ -392,45 +439,47 @@ dependencies = [
 
 [[package]]
 name = "alloy-rpc-types"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5"
+checksum = "9ffc534b7919e18f35e3aa1f507b6f3d9d92ec298463a9f6beaac112809d8d06"
 dependencies = [
+ "alloy-primitives",
  "alloy-rpc-types-anvil",
  "alloy-rpc-types-engine",
- "alloy-rpc-types-eth",
+ "alloy-rpc-types-eth 0.4.0",
  "alloy-rpc-types-trace",
  "alloy-rpc-types-txpool",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "serde",
 ]
 
 [[package]]
 name = "alloy-rpc-types-anvil"
-version = "0.3.6"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d25cb45ad7c0930dd62eecf164d2afe4c3d2dd2c82af85680ad1f118e1e5cb83"
+checksum = "142f6fb21ef1857b3d175dc16b73d67f4b70914e6898610da3c0b65a1281fe7b"
 dependencies = [
  "alloy-primitives",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "serde",
 ]
 
 [[package]]
 name = "alloy-rpc-types-engine"
-version = "0.3.6"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b"
+checksum = "c032e9b725a990be03cc0ddd9fa73c21f61d1449b328083aa22fbfafb03eda1b"
 dependencies = [
- "alloy-consensus",
- "alloy-eips",
+ "alloy-consensus 0.4.2",
+ "alloy-eips 0.4.2",
  "alloy-primitives",
  "alloy-rlp",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "derive_more 1.0.0",
  "jsonwebtoken",
  "rand",
  "serde",
+ "strum",
 ]
 
 [[package]]
@@ -439,12 +488,12 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8"
 dependencies = [
- "alloy-consensus",
- "alloy-eips",
- "alloy-network-primitives",
+ "alloy-consensus 0.3.6",
+ "alloy-eips 0.3.6",
+ "alloy-network-primitives 0.3.6",
  "alloy-primitives",
  "alloy-rlp",
- "alloy-serde",
+ "alloy-serde 0.3.6",
  "alloy-sol-types",
  "cfg-if",
  "derive_more 1.0.0",
@@ -454,15 +503,34 @@ dependencies = [
  "serde_json",
 ]
 
+[[package]]
+name = "alloy-rpc-types-eth"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e1f655dcd5e9ccf215cbffb69272698ef6b3ec76907e8937345f2a82ae04ed4"
+dependencies = [
+ "alloy-consensus 0.4.2",
+ "alloy-eips 0.4.2",
+ "alloy-network-primitives 0.4.0",
+ "alloy-primitives",
+ "alloy-rlp",
+ "alloy-serde 0.4.2",
+ "alloy-sol-types",
+ "derive_more 1.0.0",
+ "itertools 0.13.0",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "alloy-rpc-types-trace"
-version = "0.3.6"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98db35cd42c90b484377e6bc44d95377a7a38a5ebee996e67754ac0446d542ab"
+checksum = "6900c7d94528217465f6b619f03adb2eecc9682f9083d49ad7d40ec6eda0ed04"
 dependencies = [
  "alloy-primitives",
- "alloy-rpc-types-eth",
- "alloy-serde",
+ "alloy-rpc-types-eth 0.4.0",
+ "alloy-serde 0.4.2",
  "serde",
  "serde_json",
  "thiserror",
@@ -470,13 +538,13 @@ dependencies = [
 
 [[package]]
 name = "alloy-rpc-types-txpool"
-version = "0.3.6"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bac37082c3b21283b3faf5cc0e08974272aee2f756ce1adeb26db56a5fce0d5"
+checksum = "954781be5ca2e15db08d753712f494504a04771ee4296de1e834e65c105b8ec3"
 dependencies = [
  "alloy-primitives",
- "alloy-rpc-types-eth",
- "alloy-serde",
+ "alloy-rpc-types-eth 0.4.0",
+ "alloy-serde 0.4.2",
  "serde",
 ]
 
@@ -491,11 +559,22 @@ dependencies = [
  "serde_json",
 ]
 
+[[package]]
+name = "alloy-serde"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600"
+dependencies = [
+ "alloy-primitives",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "alloy-signer"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "307324cca94354cd654d6713629f0383ec037e1ff9e3e3d547212471209860c0"
+checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504"
 dependencies = [
  "alloy-dyn-abi",
  "alloy-primitives",
@@ -509,11 +588,11 @@ dependencies = [
 
 [[package]]
 name = "alloy-signer-aws"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "076be69aa26a4c500919f1ad3847662aa6d1e9bc2995e263ed826b1546d1b990"
+checksum = "417e19d9844350d11f7426d4920a5df777f8c2abbb7a70d9de6f1faf284db15b"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-network",
  "alloy-primitives",
  "alloy-signer",
@@ -527,11 +606,11 @@ dependencies = [
 
 [[package]]
 name = "alloy-signer-gcp"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cabd79d4eb954a8c2ae7889a18e2466af186ae68376251cf58525239c156ec54"
+checksum = "b6fd12ae28e8330766821058ed9a6a06ae4f9b12c776e8a7cfb16e3a954f508e"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-network",
  "alloy-primitives",
  "alloy-signer",
@@ -545,11 +624,11 @@ dependencies = [
 
 [[package]]
 name = "alloy-signer-ledger"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3df66f5ddcc32d2070485dc702f5f5fb97cfbfa817f6e2e6bac16a4e32ed44c"
+checksum = "d3a02400dea370022151f07581b73a836115c88ce4df350206653493bec024e2"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-dyn-abi",
  "alloy-network",
  "alloy-primitives",
@@ -565,18 +644,17 @@ dependencies = [
 
 [[package]]
 name = "alloy-signer-local"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fabe917ab1778e760b4701628d1cae8e028ee9d52ac6307de4e1e9286ab6b5f"
+checksum = "494e0a256f3e99f2426f994bcd1be312c02cb8f88260088dacb33a8b8936475f"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-network",
  "alloy-primitives",
  "alloy-signer",
  "async-trait",
  "coins-bip32",
  "coins-bip39",
- "elliptic-curve",
  "eth-keystore",
  "k256",
  "rand",
@@ -585,11 +663,11 @@ dependencies = [
 
 [[package]]
 name = "alloy-signer-trezor"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1068949eda889b2c052b29a6e8c7ea2ba16be6d1af83ad165fff2a4e4ad19fcd"
+checksum = "e0d91ddee2390b002148128e47902893deba353eb1b818a3afb6c960ebf4e42c"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-network",
  "alloy-primitives",
  "alloy-signer",
@@ -675,9 +753,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-transport"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9"
+checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904"
 dependencies = [
  "alloy-json-rpc",
  "base64 0.22.1",
@@ -694,9 +772,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-transport-http"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49"
+checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212"
 dependencies = [
  "alloy-json-rpc",
  "alloy-transport",
@@ -709,9 +787,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-transport-ipc"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09fd8491249f74d16ec979b1f5672377b12ebb818e6056478ffa386954dbd350"
+checksum = "b90cf9cde7f2fce617da52768ee28f522264b282d148384a4ca0ea85af04fa3a"
 dependencies = [
  "alloy-json-rpc",
  "alloy-pubsub",
@@ -730,9 +808,9 @@ dependencies = [
 
 [[package]]
 name = "alloy-transport-ws"
-version = "0.3.6"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9704761f6297fe482276bee7f77a93cb42bd541c2bd6c1c560b6f3a9ece672e"
+checksum = "7153b88690de6a50bba81c11e1d706bc41dbb90126d607404d60b763f6a3947f"
 dependencies = [
  "alloy-pubsub",
  "alloy-transport",
@@ -741,7 +819,7 @@ dependencies = [
  "rustls 0.23.13",
  "serde_json",
  "tokio",
- "tokio-tungstenite 0.23.1",
+ "tokio-tungstenite",
  "tracing",
  "ws_stream_wasm",
 ]
@@ -849,10 +927,10 @@ name = "anvil"
 version = "0.2.0"
 dependencies = [
  "alloy-chains",
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-contract",
  "alloy-dyn-abi",
- "alloy-eips",
+ "alloy-eips 0.4.2",
  "alloy-genesis",
  "alloy-json-abi",
  "alloy-json-rpc",
@@ -863,7 +941,7 @@ dependencies = [
  "alloy-rlp",
  "alloy-rpc-client",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-signer",
  "alloy-signer-local",
  "alloy-sol-types",
@@ -917,13 +995,13 @@ dependencies = [
 name = "anvil-core"
 version = "0.2.0"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-dyn-abi",
- "alloy-eips",
+ "alloy-eips 0.4.2",
  "alloy-primitives",
  "alloy-rlp",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-trie",
  "bytes",
  "foundry-common",
@@ -1601,7 +1679,7 @@ dependencies = [
  "sha1",
  "sync_wrapper 1.0.1",
  "tokio",
- "tokio-tungstenite 0.24.0",
+ "tokio-tungstenite",
  "tower 0.5.1",
  "tower-layer",
  "tower-service",
@@ -1916,9 +1994,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.1.22"
+version = "1.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
+checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938"
 dependencies = [
  "shlex",
 ]
@@ -3247,14 +3325,14 @@ name = "forge"
 version = "0.2.0"
 dependencies = [
  "alloy-chains",
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-dyn-abi",
  "alloy-json-abi",
  "alloy-network",
  "alloy-primitives",
  "alloy-provider",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-signer",
  "alloy-signer-local",
  "alloy-sol-macro-expander",
@@ -3377,15 +3455,15 @@ name = "forge-script"
 version = "0.2.0"
 dependencies = [
  "alloy-chains",
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-dyn-abi",
- "alloy-eips",
+ "alloy-eips 0.4.2",
  "alloy-json-abi",
  "alloy-network",
  "alloy-primitives",
  "alloy-provider",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-signer",
  "alloy-transport",
  "async-recursion",
@@ -3498,7 +3576,7 @@ name = "foundry-cast"
 version = "0.2.0"
 dependencies = [
  "alloy-chains",
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-contract",
  "alloy-dyn-abi",
  "alloy-json-abi",
@@ -3508,7 +3586,7 @@ dependencies = [
  "alloy-provider",
  "alloy-rlp",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-signer",
  "alloy-signer-local",
  "alloy-sol-types",
@@ -3556,7 +3634,7 @@ dependencies = [
 name = "foundry-cheatcodes"
 version = "0.2.0"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-dyn-abi",
  "alloy-genesis",
  "alloy-json-abi",
@@ -3649,7 +3727,7 @@ dependencies = [
 name = "foundry-common"
 version = "0.2.0"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-contract",
  "alloy-dyn-abi",
  "alloy-json-abi",
@@ -3659,7 +3737,7 @@ dependencies = [
  "alloy-pubsub",
  "alloy-rpc-client",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-sol-types",
  "alloy-transport",
  "alloy-transport-http",
@@ -3695,11 +3773,11 @@ dependencies = [
 name = "foundry-common-fmt"
 version = "0.2.0"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-dyn-abi",
  "alloy-primitives",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "chrono",
  "comfy-table",
  "foundry-macros",
@@ -3923,7 +4001,7 @@ dependencies = [
  "alloy-primitives",
  "alloy-provider",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-sol-types",
  "alloy-transport",
  "auto_impl",
@@ -4019,14 +4097,14 @@ dependencies = [
 
 [[package]]
 name = "foundry-fork-db"
-version = "0.3.2"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fb76083f203e341deeb4a03f9cc86df096f09c723c42e44c25052c233ed87b4"
+checksum = "2c8bdd63ecf8309c549d41a6167510da5053b13e9ab5456f06aff1070c0b642f"
 dependencies = [
  "alloy-primitives",
  "alloy-provider",
  "alloy-rpc-types",
- "alloy-serde",
+ "alloy-serde 0.4.2",
  "alloy-transport",
  "eyre",
  "futures",
@@ -4086,7 +4164,7 @@ dependencies = [
 name = "foundry-wallets"
 version = "0.2.0"
 dependencies = [
- "alloy-consensus",
+ "alloy-consensus 0.4.2",
  "alloy-dyn-abi",
  "alloy-network",
  "alloy-primitives",
@@ -4799,9 +4877,9 @@ checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a"
 
 [[package]]
 name = "httparse"
-version = "1.9.4"
+version = "1.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
 
 [[package]]
 name = "httpdate"
@@ -6015,11 +6093,11 @@ version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21aad1fbf80d2bcd7406880efc7ba109365f44bbb72896758ddcbfa46bf1592c"
 dependencies = [
- "alloy-consensus",
- "alloy-eips",
+ "alloy-consensus 0.3.6",
+ "alloy-eips 0.3.6",
  "alloy-primitives",
  "alloy-rlp",
- "alloy-serde",
+ "alloy-serde 0.3.6",
  "derive_more 1.0.0",
  "serde",
  "spin",
@@ -6031,11 +6109,11 @@ version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e281fbfc2198b7c0c16457d6524f83d192662bc9f3df70f24c3038d4521616df"
 dependencies = [
- "alloy-eips",
- "alloy-network-primitives",
+ "alloy-eips 0.3.6",
+ "alloy-network-primitives 0.3.6",
  "alloy-primitives",
- "alloy-rpc-types-eth",
- "alloy-serde",
+ "alloy-rpc-types-eth 0.3.6",
+ "alloy-serde 0.3.6",
  "cfg-if",
  "hashbrown 0.14.5",
  "op-alloy-consensus",
@@ -7101,9 +7179,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "reqwest"
-version = "0.12.7"
+version = "0.12.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
+checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
 dependencies = [
  "async-compression",
  "base64 0.22.1",
@@ -7129,8 +7207,8 @@ dependencies = [
  "pin-project-lite",
  "quinn",
  "rustls 0.23.13",
- "rustls-native-certs 0.7.3",
- "rustls-pemfile 2.1.3",
+ "rustls-native-certs 0.8.0",
+ "rustls-pemfile 2.2.0",
  "rustls-pki-types",
  "serde",
  "serde_json",
@@ -7168,12 +7246,12 @@ dependencies = [
 
 [[package]]
 name = "revm-inspectors"
-version = "0.7.7"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd8e3bae0d5c824da0ac883e2521c5e83870d6521eeeccd4ee54266aa3cc1a51"
+checksum = "43c44af0bf801f48d25f7baf25cf72aff4c02d610f83b428175228162fef0246"
 dependencies = [
  "alloy-primitives",
- "alloy-rpc-types-eth",
+ "alloy-rpc-types-eth 0.4.0",
  "alloy-rpc-types-trace",
  "alloy-sol-types",
  "anstyle",
@@ -7452,19 +7530,6 @@ dependencies = [
  "security-framework",
 ]
 
-[[package]]
-name = "rustls-native-certs"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
-dependencies = [
- "openssl-probe",
- "rustls-pemfile 2.1.3",
- "rustls-pki-types",
- "schannel",
- "security-framework",
-]
-
 [[package]]
 name = "rustls-native-certs"
 version = "0.8.0"
@@ -7472,7 +7537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
 dependencies = [
  "openssl-probe",
- "rustls-pemfile 2.1.3",
+ "rustls-pemfile 2.2.0",
  "rustls-pki-types",
  "schannel",
  "security-framework",
@@ -7489,11 +7554,10 @@ dependencies = [
 
 [[package]]
 name = "rustls-pemfile"
-version = "2.1.3"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
 dependencies = [
- "base64 0.22.1",
  "rustls-pki-types",
 ]
 
@@ -7624,9 +7688,9 @@ dependencies = [
 
 [[package]]
 name = "scc"
-version = "2.1.18"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "215b1103f73e23e9cb6883072c1fb26ae55c09d42054654955c739e5418a7c96"
+checksum = "836f1e0f4963ef5288b539b643b35e043e76a32d0f4e47e67febf69576527f50"
 dependencies = [
  "sdd",
 ]
@@ -8652,9 +8716,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-tungstenite"
-version = "0.23.1"
+version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd"
+checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
 dependencies = [
  "futures-util",
  "log",
@@ -8662,22 +8726,10 @@ dependencies = [
  "rustls-pki-types",
  "tokio",
  "tokio-rustls 0.26.0",
- "tungstenite 0.23.0",
+ "tungstenite",
  "webpki-roots",
 ]
 
-[[package]]
-name = "tokio-tungstenite"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
-dependencies = [
- "futures-util",
- "log",
- "tokio",
- "tungstenite 0.24.0",
-]
-
 [[package]]
 name = "tokio-util"
 version = "0.7.12"
@@ -8757,7 +8809,7 @@ dependencies = [
  "pin-project 1.1.5",
  "prost",
  "rustls-native-certs 0.8.0",
- "rustls-pemfile 2.1.3",
+ "rustls-pemfile 2.2.0",
  "socket2",
  "tokio",
  "tokio-rustls 0.26.0",
@@ -8982,26 +9034,6 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
-[[package]]
-name = "tungstenite"
-version = "0.23.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8"
-dependencies = [
- "byteorder",
- "bytes",
- "data-encoding",
- "http 1.1.0",
- "httparse",
- "log",
- "rand",
- "rustls 0.23.13",
- "rustls-pki-types",
- "sha1",
- "thiserror",
- "utf-8",
-]
-
 [[package]]
 name = "tungstenite"
 version = "0.24.0"
@@ -9015,6 +9047,8 @@ dependencies = [
  "httparse",
  "log",
  "rand",
+ "rustls 0.23.13",
+ "rustls-pki-types",
  "sha1",
  "thiserror",
  "utf-8",
diff --git a/Cargo.toml b/Cargo.toml
index ae6ee18250fa7..bdc2c87722340 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -168,39 +168,39 @@ foundry-linking = { path = "crates/linking" }
 # solc & compilation utilities
 foundry-block-explorers = { version = "0.7.3", default-features = false }
 foundry-compilers = { version = "0.11.3", default-features = false }
-foundry-fork-db = "0.3.2"
+foundry-fork-db = "0.4.0"
 solang-parser = "=0.3.3"
 
 ## revm
 revm = { version = "14.0.3", default-features = false }
 revm-primitives = { version = "10.0.0", default-features = false }
-revm-inspectors = { version = "0.7.7", features = ["serde"] }
+revm-inspectors = { version = "0.8.0", features = ["serde"] }
 
 ## ethers
 ethers-contract-abigen = { version = "2.0.14", default-features = false }
 
 ## alloy
-alloy-consensus = { version = "0.3.6", default-features = false }
-alloy-contract = { version = "0.3.6", default-features = false }
-alloy-eips = { version = "0.3.6", default-features = false }
-alloy-genesis = { version = "0.3.6", default-features = false }
-alloy-json-rpc = { version = "0.3.6", default-features = false }
-alloy-network = { version = "0.3.6", default-features = false }
-alloy-provider = { version = "0.3.6", default-features = false }
-alloy-pubsub = { version = "0.3.6", default-features = false }
-alloy-rpc-client = { version = "0.3.6", default-features = false }
-alloy-rpc-types = { version = "0.3.6", default-features = true }
-alloy-serde = { version = "0.3.6", default-features = false }
-alloy-signer = { version = "0.3.6", default-features = false }
-alloy-signer-aws = { version = "0.3.6", default-features = false }
-alloy-signer-gcp = { version = "0.3.6", default-features = false }
-alloy-signer-ledger = { version = "0.3.6", default-features = false }
-alloy-signer-local = { version = "0.3.6", default-features = false }
-alloy-signer-trezor = { version = "0.3.6", default-features = false }
-alloy-transport = { version = "0.3.6", default-features = false }
-alloy-transport-http = { version = "0.3.6", default-features = false }
-alloy-transport-ipc = { version = "0.3.6", default-features = false }
-alloy-transport-ws = { version = "0.3.6", default-features = false }
+alloy-consensus = { version = "0.4.2", default-features = false }
+alloy-contract = { version = "0.4.2", default-features = false }
+alloy-eips = { version = "0.4.2", default-features = false }
+alloy-genesis = { version = "0.4.2", default-features = false }
+alloy-json-rpc = { version = "0.4.2", default-features = false }
+alloy-network = { version = "0.4.2", default-features = false }
+alloy-provider = { version = "0.4.2", default-features = false }
+alloy-pubsub = { version = "0.4.2", default-features = false }
+alloy-rpc-client = { version = "0.4.2", default-features = false }
+alloy-rpc-types = { version = "0.4.2", default-features = true }
+alloy-serde = { version = "0.4.2", default-features = false }
+alloy-signer = { version = "0.4.2", default-features = false }
+alloy-signer-aws = { version = "0.4.2", default-features = false }
+alloy-signer-gcp = { version = "0.4.2", default-features = false }
+alloy-signer-ledger = { version = "0.4.2", default-features = false }
+alloy-signer-local = { version = "0.4.2", default-features = false }
+alloy-signer-trezor = { version = "0.4.2", default-features = false }
+alloy-transport = { version = "0.4.2", default-features = false }
+alloy-transport-http = { version = "0.4.2", default-features = false }
+alloy-transport-ipc = { version = "0.4.2", default-features = false }
+alloy-transport-ws = { version = "0.4.2", default-features = false }
 
 ## alloy-core
 alloy-dyn-abi = "0.8.5"
diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs
index 1b0895dcd5f57..337c5cfd8a57f 100644
--- a/crates/anvil/core/src/eth/block.rs
+++ b/crates/anvil/core/src/eth/block.rs
@@ -90,16 +90,16 @@ pub struct PartialHeader {
     pub logs_bloom: Bloom,
     pub difficulty: U256,
     pub number: u64,
-    pub gas_limit: u128,
-    pub gas_used: u128,
+    pub gas_limit: u64,
+    pub gas_used: u64,
     pub timestamp: u64,
     pub extra_data: Bytes,
     pub mix_hash: B256,
-    pub blob_gas_used: Option<u128>,
-    pub excess_blob_gas: Option<u128>,
+    pub blob_gas_used: Option<u64>,
+    pub excess_blob_gas: Option<u64>,
     pub parent_beacon_block_root: Option<B256>,
     pub nonce: B64,
-    pub base_fee: Option<u128>,
+    pub base_fee: Option<u64>,
 }
 
 impl From<Header> for PartialHeader {
@@ -150,7 +150,7 @@ mod tests {
             difficulty: Default::default(),
             number: 124u64,
             gas_limit: Default::default(),
-            gas_used: 1337u128,
+            gas_used: 1337u64,
             timestamp: 0,
             extra_data: Default::default(),
             mix_hash: Default::default(),
@@ -167,7 +167,7 @@ mod tests {
         let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap();
         assert_eq!(header, decoded);
 
-        header.base_fee_per_gas = Some(12345u128);
+        header.base_fee_per_gas = Some(12345u64);
 
         let encoded = alloy_rlp::encode(&header);
         let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap();
@@ -190,8 +190,8 @@ mod tests {
             logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(),
             difficulty: U256::from(2222),
             number: 0xd05u64,
-            gas_limit: 0x115cu128,
-            gas_used: 0x15b3u128,
+            gas_limit: 0x115cu64,
+            gas_used: 0x15b3u64,
             timestamp: 0x1a0au64,
             extra_data: hex::decode("7788").unwrap().into(),
             mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
@@ -223,8 +223,8 @@ mod tests {
             logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(),
             difficulty: U256::from(2222),
             number: 0xd05u64,
-            gas_limit: 0x115cu128,
-            gas_used: 0x15b3u128,
+            gas_limit: 0x115cu64,
+            gas_used: 0x15b3u64,
             timestamp: 0x1a0au64,
             extra_data: hex::decode("7788").unwrap().into(),
             mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
@@ -255,8 +255,8 @@ mod tests {
             logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(),
             difficulty: U256::from(0x020000),
             number: 1u64,
-            gas_limit: U256::from(0x016345785d8a0000u128).to::<u128>(),
-            gas_used: U256::from(0x015534).to::<u128>(),
+            gas_limit: U256::from(0x016345785d8a0000u128).to::<u64>(),
+            gas_used: U256::from(0x015534).to::<u64>(),
             timestamp: 0x079e,
             extra_data: hex::decode("42").unwrap().into(),
             mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs
index e03a862b86ab3..2ed75db28f14d 100644
--- a/crates/anvil/core/src/eth/transaction/mod.rs
+++ b/crates/anvil/core/src/eth/transaction/mod.rs
@@ -498,7 +498,7 @@ impl PendingTransaction {
                     value: (*value),
                     gas_price: U256::from(*gas_price),
                     gas_priority_fee: None,
-                    gas_limit: *gas_limit as u64,
+                    gas_limit: *gas_limit,
                     access_list: vec![],
                     ..Default::default()
                 }
@@ -524,7 +524,7 @@ impl PendingTransaction {
                     value: *value,
                     gas_price: U256::from(*gas_price),
                     gas_priority_fee: None,
-                    gas_limit: *gas_limit as u64,
+                    gas_limit: *gas_limit,
                     access_list: access_list.clone().into(),
                     ..Default::default()
                 }
@@ -551,7 +551,7 @@ impl PendingTransaction {
                     value: *value,
                     gas_price: U256::from(*max_fee_per_gas),
                     gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)),
-                    gas_limit: *gas_limit as u64,
+                    gas_limit: *gas_limit,
                     access_list: access_list.clone().into(),
                     ..Default::default()
                 }
@@ -582,7 +582,7 @@ impl PendingTransaction {
                     gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)),
                     max_fee_per_blob_gas: Some(U256::from(*max_fee_per_blob_gas)),
                     blob_hashes: blob_versioned_hashes.clone(),
-                    gas_limit: *gas_limit as u64,
+                    gas_limit: *gas_limit,
                     access_list: access_list.clone().into(),
                     ..Default::default()
                 }
@@ -609,7 +609,7 @@ impl PendingTransaction {
                     value: *value,
                     gas_price: U256::from(*max_fee_per_gas),
                     gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)),
-                    gas_limit: *gas_limit as u64,
+                    gas_limit: *gas_limit,
                     access_list: access_list.clone().into(),
                     authorization_list: Some(authorization_list.clone().into()),
                     ..Default::default()
@@ -637,7 +637,7 @@ impl PendingTransaction {
                     value: *value,
                     gas_price: U256::ZERO,
                     gas_priority_fee: None,
-                    gas_limit: *gas_limit as u64,
+                    gas_limit: { *gas_limit },
                     access_list: vec![],
                     optimism: OptimismFields {
                         source_hash: Some(*source_hash),
@@ -716,7 +716,7 @@ impl TypedTransaction {
         }
     }
 
-    pub fn gas_limit(&self) -> u128 {
+    pub fn gas_limit(&self) -> u64 {
         match self {
             Self::Legacy(tx) => tx.tx().gas_limit,
             Self::EIP2930(tx) => tx.tx().gas_limit,
@@ -766,20 +766,23 @@ impl TypedTransaction {
     /// and if the transaction is EIP-4844, the result of (total blob gas cost * max fee per blob
     /// gas) is also added
     pub fn max_cost(&self) -> u128 {
-        let mut max_cost = self.gas_limit().saturating_mul(self.gas_price());
+        let mut max_cost = (self.gas_limit() as u128).saturating_mul(self.gas_price());
 
         if self.is_eip4844() {
             max_cost = max_cost.saturating_add(
-                self.blob_gas().unwrap_or(0).mul(self.max_fee_per_blob_gas().unwrap_or(0)),
+                self.blob_gas()
+                    .map(|g| g as u128)
+                    .unwrap_or(0)
+                    .mul(self.max_fee_per_blob_gas().unwrap_or(0)),
             )
         }
 
         max_cost
     }
 
-    pub fn blob_gas(&self) -> Option<u128> {
+    pub fn blob_gas(&self) -> Option<u64> {
         match self {
-            Self::EIP4844(tx) => Some(tx.tx().tx().blob_gas() as u128),
+            Self::EIP4844(tx) => Some(tx.tx().tx().blob_gas()),
             _ => None,
         }
     }
@@ -1256,7 +1259,7 @@ pub struct TransactionEssentials {
     pub kind: TxKind,
     pub input: Bytes,
     pub nonce: u64,
-    pub gas_limit: u128,
+    pub gas_limit: u64,
     pub gas_price: Option<u128>,
     pub max_fee_per_gas: Option<u128>,
     pub max_priority_fee_per_gas: Option<u128>,
@@ -1279,7 +1282,7 @@ pub struct TransactionInfo {
     pub exit: InstructionResult,
     pub out: Option<Bytes>,
     pub nonce: u64,
-    pub gas_used: u128,
+    pub gas_used: u64,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
@@ -1287,9 +1290,9 @@ pub struct TransactionInfo {
 pub struct DepositReceipt<T = alloy_primitives::Log> {
     #[serde(flatten)]
     pub inner: ReceiptWithBloom<T>,
-    #[serde(default, with = "alloy_serde::num::u64_opt_via_ruint")]
+    #[serde(default, with = "alloy_serde::quantity::opt")]
     pub deposit_nonce: Option<u64>,
-    #[serde(default, with = "alloy_serde::num::u64_opt_via_ruint")]
+    #[serde(default, with = "alloy_serde::quantity::opt")]
     pub deposit_receipt_version: Option<u64>,
 }
 
@@ -1690,7 +1693,7 @@ mod tests {
         let tx = TxLegacy {
             nonce: 2u64,
             gas_price: 1000000000u128,
-            gas_limit: 100000u128,
+            gas_limit: 100000,
             to: TxKind::Call(Address::from_slice(
                 &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..],
             )),
diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs
index 6cc7bfa5aa63c..170d67f1d9ac1 100644
--- a/crates/anvil/core/src/eth/transaction/optimism.rs
+++ b/crates/anvil/core/src/eth/transaction/optimism.rs
@@ -16,7 +16,7 @@ pub struct DepositTransactionRequest {
     pub kind: TxKind,
     pub mint: U256,
     pub value: U256,
-    pub gas_limit: u128,
+    pub gas_limit: u64,
     pub is_system_tx: bool,
     pub input: Bytes,
 }
@@ -178,7 +178,7 @@ impl Transaction for DepositTransactionRequest {
     }
 
     /// Get `gas_limit`.
-    fn gas_limit(&self) -> u128 {
+    fn gas_limit(&self) -> u64 {
         self.gas_limit
     }
 
@@ -281,7 +281,7 @@ pub struct DepositTransaction {
     pub kind: TxKind,
     pub mint: U256,
     pub value: U256,
-    pub gas_limit: u128,
+    pub gas_limit: u64,
     pub is_system_tx: bool,
     pub input: Bytes,
 }
diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs
index 3b7778a722468..149b9c8630659 100644
--- a/crates/anvil/src/cmd.rs
+++ b/crates/anvil/src/cmd.rs
@@ -545,7 +545,7 @@ pub struct AnvilEvmArgs {
         value_name = "FEE",
         help_heading = "Environment config"
     )]
-    pub block_base_fee_per_gas: Option<u128>,
+    pub block_base_fee_per_gas: Option<u64>,
 
     /// The chain ID.
     #[arg(long, alias = "chain", help_heading = "Environment config")]
diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs
index f3dbf21f6013c..189cf51752d1b 100644
--- a/crates/anvil/src/config.rs
+++ b/crates/anvil/src/config.rs
@@ -98,7 +98,7 @@ pub struct NodeConfig {
     /// Default gas price for all txs
     pub gas_price: Option<u128>,
     /// Default base fee
-    pub base_fee: Option<u128>,
+    pub base_fee: Option<u64>,
     /// Default blob excess gas and price
     pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
     /// The hardfork to use
@@ -474,9 +474,9 @@ impl NodeConfig {
         self
     }
     /// Returns the base fee to use
-    pub fn get_base_fee(&self) -> u128 {
+    pub fn get_base_fee(&self) -> u64 {
         self.base_fee
-            .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas))
+            .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(|g| g as u64)))
             .unwrap_or(INITIAL_BASE_FEE)
     }
 
@@ -618,7 +618,7 @@ impl NodeConfig {
 
     /// Sets the base fee
     #[must_use]
-    pub fn with_base_fee(mut self, base_fee: Option<u128>) -> Self {
+    pub fn with_base_fee(mut self, base_fee: Option<u64>) -> Self {
         self.base_fee = base_fee;
         self
     }
@@ -1183,7 +1183,7 @@ latest block number: {latest_block}"
                 // this is the base fee of the current block, but we need the base fee of
                 // the next block
                 let next_block_base_fee = fees.get_next_block_base_fee_per_gas(
-                    block.header.gas_used,
+                    block.header.gas_used as u128,
                     gas_limit,
                     block.header.base_fee_per_gas.unwrap_or_default(),
                 );
@@ -1195,9 +1195,9 @@ latest block number: {latest_block}"
                 (block.header.excess_blob_gas, block.header.blob_gas_used)
             {
                 env.block.blob_excess_gas_and_price =
-                    Some(BlobExcessGasAndPrice::new(blob_excess_gas as u64));
-                let next_block_blob_excess_gas =
-                    fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used);
+                    Some(BlobExcessGasAndPrice::new(blob_excess_gas));
+                let next_block_blob_excess_gas = fees
+                    .get_next_block_blob_excess_gas(blob_excess_gas as u128, blob_gas_used as u128);
                 fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
                     next_block_blob_excess_gas,
                 ));
@@ -1257,13 +1257,13 @@ latest block number: {latest_block}"
             chain_id,
             override_chain_id,
             timestamp: block.header.timestamp,
-            base_fee: block.header.base_fee_per_gas,
+            base_fee: block.header.base_fee_per_gas.map(|g| g as u128),
             timeout: self.fork_request_timeout,
             retries: self.fork_request_retries,
             backoff: self.fork_retry_backoff,
             compute_units_per_second: self.compute_units_per_second,
             total_difficulty: block.header.total_difficulty.unwrap_or_default(),
-            blob_gas_used: block.header.blob_gas_used,
+            blob_gas_used: block.header.blob_gas_used.map(|g| g as u128),
             blob_excess_gas_and_price: env.block.blob_excess_gas_and_price.clone(),
             force_transactions,
         };
@@ -1284,7 +1284,7 @@ latest block number: {latest_block}"
             if let Some(gas_limit) = self.gas_limit {
                 return gas_limit;
             } else if block.header.gas_limit > 0 {
-                return block.header.gas_limit;
+                return block.header.gas_limit as u128;
             }
         }
 
diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs
index 85d1aceaf5318..c8fd497a3e815 100644
--- a/crates/anvil/src/eth/api.rs
+++ b/crates/anvil/src/eth/api.rs
@@ -592,7 +592,7 @@ impl EthApi {
     /// Returns the current gas price
     pub fn gas_price(&self) -> u128 {
         if self.backend.is_eip1559() {
-            self.backend.base_fee().saturating_add(self.lowest_suggestion_tip())
+            (self.backend.base_fee() as u128).saturating_add(self.lowest_suggestion_tip())
         } else {
             self.backend.fees().raw_gas_price()
         }
@@ -1405,7 +1405,7 @@ impl EthApi {
         // The spec states that `base_fee_per_gas` "[..] includes the next block after the
         // newest of the returned range, because this value can be derived from the
         // newest block"
-        response.base_fee_per_gas.push(self.backend.fees().base_fee());
+        response.base_fee_per_gas.push(self.backend.fees().base_fee() as u128);
 
         // Same goes for the `base_fee_per_blob_gas`:
         // > [..] includes the next block after the newest of the returned range, because this
@@ -2302,7 +2302,7 @@ impl EthApi {
             let to = tx.to();
             let gas_price = tx.gas_price();
             let value = tx.value();
-            let gas = tx.gas_limit();
+            let gas = tx.gas_limit() as u128;
             TxpoolInspectSummary { to, value, gas, gas_price }
         }
 
@@ -2485,7 +2485,8 @@ impl EthApi {
 
         // get the highest possible gas limit, either the request's set value or the currently
         // configured gas limit
-        let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit.to());
+        let mut highest_gas_limit =
+            request.gas.map_or(block_env.gas_limit.to::<u128>(), |g| g as u128);
 
         let gas_price = fees.gas_price.unwrap_or_default();
         // If we have non-zero gas price, cap gas limit by sender balance
@@ -2507,7 +2508,7 @@ impl EthApi {
         }
 
         let mut call_to_estimate = request.clone();
-        call_to_estimate.gas = Some(highest_gas_limit);
+        call_to_estimate.gas = Some(highest_gas_limit as u64);
 
         // execute the call without writing to db
         let ethres =
@@ -2541,7 +2542,7 @@ impl EthApi {
 
         // Binary search for the ideal gas limit
         while (highest_gas_limit - lowest_gas_limit) > 1 {
-            request.gas = Some(mid_gas_limit);
+            request.gas = Some(mid_gas_limit as u64);
             let ethres = self.backend.call_with_state(
                 &state,
                 request.clone(),
@@ -2688,7 +2689,7 @@ impl EthApi {
         let max_fee_per_blob_gas = request.max_fee_per_blob_gas;
         let gas_price = request.gas_price;
 
-        let gas_limit = request.gas.unwrap_or(self.backend.gas_limit());
+        let gas_limit = request.gas.unwrap_or(self.backend.gas_limit() as u64);
 
         let request = match transaction_request_to_typed(request) {
             Some(TypedTransactionRequest::Legacy(mut m)) => {
diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs
index caa042bf48861..3221620ef0e88 100644
--- a/crates/anvil/src/eth/backend/executor.rs
+++ b/crates/anvil/src/eth/backend/executor.rs
@@ -28,8 +28,9 @@ use foundry_evm::{
         },
     },
     traces::CallTraceNode,
+    utils::alphanet_handler_register,
 };
-use revm::primitives::MAX_BLOB_GAS_PER_BLOCK;
+use revm::{db::WrapDatabaseRef, primitives::MAX_BLOB_GAS_PER_BLOCK};
 use std::sync::Arc;
 
 /// Represents an executed transaction (transacted on the DB)
@@ -38,7 +39,7 @@ pub struct ExecutedTransaction {
     transaction: Arc<PoolTransaction>,
     exit_reason: InstructionResult,
     out: Option<Output>,
-    gas_used: u128,
+    gas_used: u64,
     logs: Vec<Log>,
     traces: Vec<CallTraceNode>,
     nonce: u64,
@@ -48,7 +49,7 @@ pub struct ExecutedTransaction {
 
 impl ExecutedTransaction {
     /// Creates the receipt for the transaction
-    fn create_receipt(&self, cumulative_gas_used: &mut u128) -> TypedReceipt {
+    fn create_receipt(&self, cumulative_gas_used: &mut u64) -> TypedReceipt {
         let logs = self.logs.clone();
         *cumulative_gas_used = cumulative_gas_used.saturating_add(self.gas_used);
 
@@ -56,7 +57,7 @@ impl ExecutedTransaction {
         let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8);
         let receipt_with_bloom: ReceiptWithBloom = Receipt {
             status: (status_code == 1).into(),
-            cumulative_gas_used: *cumulative_gas_used,
+            cumulative_gas_used: *cumulative_gas_used as u128,
             logs,
         }
         .into();
@@ -101,9 +102,9 @@ pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
     pub cfg_env: CfgEnvWithHandlerCfg,
     pub parent_hash: B256,
     /// Cumulative gas used by all executed transactions
-    pub gas_used: u128,
+    pub gas_used: u64,
     /// Cumulative blob gas used by all executed transactions
-    pub blob_gas_used: u128,
+    pub blob_gas_used: u64,
     pub enable_steps_tracing: bool,
     pub alphanet: bool,
     pub print_logs: bool,
@@ -118,24 +119,24 @@ impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V
         let mut transaction_infos = Vec::new();
         let mut receipts = Vec::new();
         let mut bloom = Bloom::default();
-        let mut cumulative_gas_used: u128 = 0;
+        let mut cumulative_gas_used = 0u64;
         let mut invalid = Vec::new();
         let mut included = Vec::new();
-        let gas_limit = self.block_env.gas_limit.to::<u128>();
+        let gas_limit = self.block_env.gas_limit.to::<u64>();
         let parent_hash = self.parent_hash;
         let block_number = self.block_env.number.to::<u64>();
         let difficulty = self.block_env.difficulty;
         let beneficiary = self.block_env.coinbase;
         let timestamp = self.block_env.timestamp.to::<u64>();
         let base_fee = if self.cfg_env.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) {
-            Some(self.block_env.basefee.to::<u128>())
+            Some(self.block_env.basefee.to::<u64>())
         } else {
             None
         };
 
         let is_cancun = self.cfg_env.handler_cfg.spec_id >= SpecId::CANCUN;
         let excess_blob_gas = if is_cancun { self.block_env.get_blob_excess_gas() } else { None };
-        let mut cumulative_blob_gas_used = if is_cancun { Some(0u128) } else { None };
+        let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
 
         for tx in self.into_iter() {
             let tx = match tx {
@@ -172,7 +173,7 @@ impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V
                     .blob_gas()
                     .unwrap_or(0);
                 cumulative_blob_gas_used =
-                    Some(cumulative_blob_gas_used.unwrap_or(0u128).saturating_add(tx_blob_gas));
+                    Some(cumulative_blob_gas_used.unwrap_or(0u64).saturating_add(tx_blob_gas));
             }
             let receipt = tx.create_receipt(&mut cumulative_gas_used);
 
@@ -228,7 +229,7 @@ impl<'a, DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'a, DB, V
             base_fee,
             parent_beacon_block_root: Default::default(),
             blob_gas_used: cumulative_blob_gas_used,
-            excess_blob_gas: excess_blob_gas.map(|g| g as u128),
+            excess_blob_gas,
         };
 
         let block = Block::new(partial_header, transactions.clone(), ommers);
@@ -277,16 +278,16 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator
         let env = self.env_for(&transaction.pending_transaction);
 
         // check that we comply with the block's gas limit, if not disabled
-        let max_gas = self.gas_used.saturating_add(env.tx.gas_limit as u128);
-        if !env.cfg.disable_block_gas_limit && max_gas > env.block.gas_limit.to::<u128>() {
+        let max_gas = self.gas_used.saturating_add(env.tx.gas_limit);
+        if !env.cfg.disable_block_gas_limit && max_gas > env.block.gas_limit.to::<u64>() {
             return Some(TransactionExecutionOutcome::Exhausted(transaction))
         }
 
         // check that we comply with the block's blob gas limit
         let max_blob_gas = self.blob_gas_used.saturating_add(
-            transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0u128),
+            transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0),
         );
-        if max_blob_gas > MAX_BLOB_GAS_PER_BLOCK as u128 {
+        if max_blob_gas > MAX_BLOB_GAS_PER_BLOCK {
             return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction))
         }
 
@@ -303,7 +304,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator
         let nonce = account.nonce;
 
         // records all call and step traces
-        let mut inspector = Inspector::default().with_tracing().with_alphanet(self.alphanet);
+        let mut inspector = Inspector::default().with_tracing();
         if self.enable_steps_tracing {
             inspector = inspector.with_steps_tracing();
         }
@@ -312,8 +313,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator
         }
 
         let exec_result = {
-            let mut evm =
-                foundry_evm::utils::new_evm_with_inspector(&mut *self.db, env, &mut inspector);
+            let mut evm = new_evm_with_inspector(&mut *self.db, env, &mut inspector, self.alphanet);
             if let Some(factory) = &self.precompile_factory {
                 inject_precompiles(&mut evm, factory.precompiles());
             }
@@ -364,7 +364,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator
         trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out);
 
         // Track the total gas used for total gas per block checks
-        self.gas_used = self.gas_used.saturating_add(gas_used as u128);
+        self.gas_used = self.gas_used.saturating_add(gas_used);
 
         // Track the total blob gas used for total blob gas per blob checks
         if let Some(blob_gas) = transaction.pending_transaction.transaction.transaction.blob_gas() {
@@ -377,7 +377,7 @@ impl<'a, 'b, DB: Db + ?Sized, V: TransactionValidator> Iterator
             transaction,
             exit_reason,
             out,
-            gas_used: gas_used as u128,
+            gas_used,
             logs: logs.unwrap_or_default(),
             traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(),
             nonce,
@@ -396,3 +396,37 @@ fn build_logs_bloom(logs: Vec<Log>, bloom: &mut Bloom) {
         }
     }
 }
+
+/// Creates a database with given database and inspector, optionally enabling alphanet features.
+pub fn new_evm_with_inspector<DB: revm::Database>(
+    db: DB,
+    env: EnvWithHandlerCfg,
+    inspector: &mut dyn revm::Inspector<DB>,
+    alphanet: bool,
+) -> revm::Evm<'_, &mut dyn revm::Inspector<DB>, DB> {
+    let EnvWithHandlerCfg { env, handler_cfg } = env;
+
+    let mut handler = revm::Handler::new(handler_cfg);
+
+    handler.append_handler_register_plain(revm::inspector_handle_register);
+    if alphanet {
+        handler.append_handler_register_plain(alphanet_handler_register);
+    }
+
+    let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector);
+
+    revm::Evm::new(context, handler)
+}
+
+/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`.
+pub fn new_evm_with_inspector_ref<'a, DB>(
+    db: DB,
+    env: EnvWithHandlerCfg,
+    inspector: &mut dyn revm::Inspector<WrapDatabaseRef<DB>>,
+    alphanet: bool,
+) -> revm::Evm<'a, &mut dyn revm::Inspector<WrapDatabaseRef<DB>>, WrapDatabaseRef<DB>>
+where
+    DB: revm::DatabaseRef,
+{
+    new_evm_with_inspector(WrapDatabaseRef(db), env, inspector, alphanet)
+}
diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs
index ea41b85a954c4..7d887f697572b 100644
--- a/crates/anvil/src/eth/backend/fork.rs
+++ b/crates/anvil/src/eth/backend/fork.rs
@@ -92,7 +92,13 @@ impl ClientFork {
         let total_difficulty = block.header.total_difficulty.unwrap_or_default();
 
         let number = block.header.number;
-        self.config.write().update_block(number, block_hash, timestamp, base_fee, total_difficulty);
+        self.config.write().update_block(
+            number,
+            block_hash,
+            timestamp,
+            base_fee.map(|g| g as u128),
+            total_difficulty,
+        );
 
         self.clear_cached_storage();
 
@@ -202,7 +208,7 @@ impl ClientFork {
         let block = block.unwrap_or_default();
         let res = self.provider().estimate_gas(request).block(block.into()).await?;
 
-        Ok(res)
+        Ok(res as u128)
     }
 
     /// Sends `eth_createAccessList`
diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs
index b354a9a5c1236..e590d57e35bbb 100644
--- a/crates/anvil/src/eth/backend/mem/inspector.rs
+++ b/crates/anvil/src/eth/backend/mem/inspector.rs
@@ -14,7 +14,6 @@ use foundry_evm::{
         EvmContext,
     },
     traces::TracingInspectorConfig,
-    InspectorExt,
 };
 
 /// The [`revm::Inspector`] used when transacting in the evm
@@ -23,8 +22,6 @@ pub struct Inspector {
     pub tracer: Option<TracingInspector>,
     /// collects all `console.sol` logs
     pub log_collector: Option<LogCollector>,
-    /// Whether to enable Alphanet support
-    pub alphanet: bool,
 }
 
 impl Inspector {
@@ -59,12 +56,6 @@ impl Inspector {
         self.log_collector = Some(Default::default());
         self
     }
-
-    /// Enables Alphanet features
-    pub fn with_alphanet(mut self, yes: bool) -> Self {
-        self.alphanet = yes;
-        self
-    }
 }
 
 impl<DB: Database> revm::Inspector<DB> for Inspector {
@@ -176,12 +167,6 @@ impl<DB: Database> revm::Inspector<DB> for Inspector {
     }
 }
 
-impl<DB: Database> InspectorExt<DB> for Inspector {
-    fn is_alphanet(&self) -> bool {
-        self.alphanet
-    }
-}
-
 /// Prints all the logs
 pub fn print_logs(logs: &[Log]) {
     for log in decode_console_logs(logs) {
diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs
index 83d8c1871fbf3..dbf22fbebe7fd 100644
--- a/crates/anvil/src/eth/backend/mem/mod.rs
+++ b/crates/anvil/src/eth/backend/mem/mod.rs
@@ -1,6 +1,7 @@
 //! In-memory blockchain backend.
 
 use self::state::trie_storage;
+use super::executor::new_evm_with_inspector_ref;
 use crate::{
     config::PruneStateHistoryConfig,
     eth::{
@@ -32,6 +33,7 @@ use crate::{
     revm::{db::DatabaseRef, primitives::AccountInfo},
     ForkChoice, NodeConfig, PrecompileFactory,
 };
+use alloy_chains::NamedChain;
 use alloy_consensus::{Account, Header, Receipt, ReceiptWithBloom};
 use alloy_eips::eip4844::MAX_BLOBS_PER_BLOCK;
 use alloy_primitives::{keccak256, Address, Bytes, TxHash, TxKind, B256, U256, U64};
@@ -64,8 +66,6 @@ use anvil_core::eth::{
     utils::meets_eip155,
 };
 use anvil_rpc::error::RpcError;
-
-use alloy_chains::NamedChain;
 use flate2::{read::GzDecoder, write::GzEncoder, Compression};
 use foundry_evm::{
     backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction},
@@ -81,8 +81,6 @@ use foundry_evm::{
         },
     },
     traces::TracingInspectorConfig,
-    utils::new_evm_with_inspector_ref,
-    InspectorExt,
 };
 use futures::channel::mpsc::{unbounded, UnboundedSender};
 use parking_lot::{Mutex, RwLock};
@@ -460,7 +458,7 @@ impl Backend {
                     // this is the base fee of the current block, but we need the base fee of
                     // the next block
                     let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
-                        fork_block.header.gas_used,
+                        fork_block.header.gas_used as u128,
                         gas_limit,
                         fork_block.header.base_fee_per_gas.unwrap_or_default(),
                     );
@@ -665,7 +663,7 @@ impl Backend {
     }
 
     /// Returns the current base fee
-    pub fn base_fee(&self) -> u128 {
+    pub fn base_fee(&self) -> u64 {
         self.fees.base_fee()
     }
 
@@ -674,7 +672,7 @@ impl Backend {
     }
 
     /// Sets the current basefee
-    pub fn set_base_fee(&self, basefee: u128) {
+    pub fn set_base_fee(&self, basefee: u64) {
         self.fees.set_base_fee(basefee)
     }
 
@@ -864,15 +862,15 @@ impl Backend {
         &self,
         db: &'db dyn DatabaseRef<Error = DatabaseError>,
         env: EnvWithHandlerCfg,
-        inspector: &'i mut dyn InspectorExt<
+        inspector: &'i mut dyn revm::Inspector<
             WrapDatabaseRef<&'db dyn DatabaseRef<Error = DatabaseError>>,
         >,
     ) -> revm::Evm<
         '_,
-        &'i mut dyn InspectorExt<WrapDatabaseRef<&'db dyn DatabaseRef<Error = DatabaseError>>>,
+        &'i mut dyn revm::Inspector<WrapDatabaseRef<&'db dyn DatabaseRef<Error = DatabaseError>>>,
         WrapDatabaseRef<&'db dyn DatabaseRef<Error = DatabaseError>>,
     > {
-        let mut evm = new_evm_with_inspector_ref(db, env, inspector);
+        let mut evm = new_evm_with_inspector_ref(db, env, inspector, self.alphanet);
         if let Some(factory) = &self.precompile_factory {
             inject_precompiles(&mut evm, factory.precompiles());
         }
@@ -1121,13 +1119,13 @@ impl Backend {
             (outcome, header, block_hash)
         };
         let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
-            header.gas_used,
-            header.gas_limit,
+            header.gas_used as u128,
+            header.gas_limit as u128,
             header.base_fee_per_gas.unwrap_or_default(),
         );
         let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
-            header.excess_blob_gas.unwrap_or_default(),
-            header.blob_gas_used.unwrap_or_default(),
+            header.excess_blob_gas.map(|g| g as u128).unwrap_or_default(),
+            header.blob_gas_used.map(|g| g as u128).unwrap_or_default(),
         );
 
         // update next base fee
@@ -1231,7 +1229,7 @@ impl Backend {
         env.tx =
             TxEnv {
                 caller,
-                gas_limit: gas_limit as u64,
+                gas_limit,
                 gas_price: U256::from(gas_price),
                 gas_priority_fee: max_priority_fee_per_gas.map(U256::from),
                 max_fee_per_blob_gas: max_fee_per_blob_gas
@@ -1269,7 +1267,7 @@ impl Backend {
 
     /// Builds [`Inspector`] with the configured options
     fn build_inspector(&self) -> Inspector {
-        let mut inspector = Inspector::default().with_alphanet(self.alphanet);
+        let mut inspector = Inspector::default();
 
         if self.print_logs {
             inspector = inspector.with_log_collector();
@@ -2230,7 +2228,7 @@ impl Backend {
 
         // Cancun specific
         let excess_blob_gas = block.header.excess_blob_gas;
-        let blob_gas_price = calc_blob_gasprice(excess_blob_gas.map_or(0, |g| g as u64));
+        let blob_gas_price = calc_blob_gasprice(excess_blob_gas.unwrap_or_default());
         let blob_gas_used = transaction.blob_gas();
 
         let effective_gas_price = match transaction.transaction {
@@ -2239,17 +2237,17 @@ impl Backend {
             TypedTransaction::EIP1559(t) => block
                 .header
                 .base_fee_per_gas
-                .unwrap_or_else(|| self.base_fee())
+                .map_or(self.base_fee() as u128, |g| g as u128)
                 .saturating_add(t.tx().max_priority_fee_per_gas),
             TypedTransaction::EIP4844(t) => block
                 .header
                 .base_fee_per_gas
-                .unwrap_or_else(|| self.base_fee())
+                .map_or(self.base_fee() as u128, |g| g as u128)
                 .saturating_add(t.tx().tx().max_priority_fee_per_gas),
             TypedTransaction::EIP7702(t) => block
                 .header
                 .base_fee_per_gas
-                .unwrap_or_else(|| self.base_fee())
+                .map_or(self.base_fee() as u128, |g| g as u128)
                 .saturating_add(t.tx().max_priority_fee_per_gas),
             TypedTransaction::Deposit(_) => 0_u128,
         };
@@ -2298,7 +2296,7 @@ impl Backend {
             transaction_hash: info.transaction_hash,
             transaction_index: Some(info.transaction_index),
             block_number: Some(block.header.number),
-            gas_used: info.gas_used,
+            gas_used: info.gas_used as u128,
             contract_address: info.contract_address,
             effective_gas_price,
             block_hash: Some(block_hash),
@@ -2306,7 +2304,7 @@ impl Backend {
             to: info.to,
             state_root: Some(block.header.state_root),
             blob_gas_price: Some(blob_gas_price),
-            blob_gas_used,
+            blob_gas_used: blob_gas_used.map(|g| g as u128),
             authorization_list: None,
         };
 
@@ -2634,7 +2632,7 @@ impl TransactionValidator for Backend {
             }
         }
 
-        if tx.gas_limit() < MIN_TRANSACTION_GAS {
+        if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
             warn!(target: "backend", "[{:?}] gas too low", tx.hash());
             return Err(InvalidTransactionError::GasTooLow);
         }
@@ -2760,7 +2758,7 @@ pub fn transaction_build(
     eth_transaction: MaybeImpersonatedTransaction,
     block: Option<&Block>,
     info: Option<TransactionInfo>,
-    base_fee: Option<u128>,
+    base_fee: Option<u64>,
 ) -> WithOtherFields<Transaction> {
     let mut transaction: Transaction = eth_transaction.clone().into();
     if info.is_some() && transaction.transaction_type == Some(0x7E) {
@@ -2774,7 +2772,7 @@ pub fn transaction_build(
         } else {
             // if transaction is already mined, gas price is considered base fee + priority fee: the
             // effective gas price.
-            let base_fee = base_fee.unwrap_or(0u128);
+            let base_fee = base_fee.map_or(0u128, |g| g as u128);
             let max_priority_fee_per_gas = transaction.max_priority_fee_per_gas.unwrap_or(0);
             transaction.gas_price = Some(base_fee.saturating_add(max_priority_fee_per_gas));
         }
diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs
index abc520c3b0141..29ae472071e7a 100644
--- a/crates/anvil/src/eth/backend/mem/storage.rs
+++ b/crates/anvil/src/eth/backend/mem/storage.rs
@@ -262,16 +262,16 @@ pub struct BlockchainStorage {
 
 impl BlockchainStorage {
     /// Creates a new storage with a genesis block
-    pub fn new(env: &Env, base_fee: Option<u128>, timestamp: u64) -> Self {
+    pub fn new(env: &Env, base_fee: Option<u64>, timestamp: u64) -> Self {
         // create a dummy genesis block
         let partial_header = PartialHeader {
             timestamp,
             base_fee,
-            gas_limit: env.block.gas_limit.to::<u128>(),
+            gas_limit: env.block.gas_limit.to::<u64>(),
             beneficiary: env.block.coinbase,
             difficulty: env.block.difficulty,
             blob_gas_used: env.block.blob_excess_gas_and_price.as_ref().map(|_| 0),
-            excess_blob_gas: env.block.get_blob_excess_gas().map(|v| v as u128),
+            excess_blob_gas: env.block.get_blob_excess_gas(),
             ..Default::default()
         };
         let block = Block::new::<MaybeImpersonatedTransaction>(partial_header, vec![], vec![]);
@@ -423,7 +423,7 @@ pub struct Blockchain {
 
 impl Blockchain {
     /// Creates a new storage with a genesis block
-    pub fn new(env: &Env, base_fee: Option<u128>, timestamp: u64) -> Self {
+    pub fn new(env: &Env, base_fee: Option<u64>, timestamp: u64) -> Self {
         Self { storage: Arc::new(RwLock::new(BlockchainStorage::new(env, base_fee, timestamp))) }
     }
 
@@ -711,7 +711,7 @@ mod tests {
         load_storage.load_transactions(serialized_transactions);
 
         let loaded_block = load_storage.blocks.get(&block_hash).unwrap();
-        assert_eq!(loaded_block.header.gas_limit, partial_header.gas_limit);
+        assert_eq!(loaded_block.header.gas_limit, { partial_header.gas_limit });
         let loaded_tx = loaded_block.transactions.first().unwrap();
         assert_eq!(loaded_tx, &tx);
     }
diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs
index ada9e6c532334..31d0521bb8543 100644
--- a/crates/anvil/src/eth/error.rs
+++ b/crates/anvil/src/eth/error.rs
@@ -388,7 +388,7 @@ impl<T: Serialize> ToRpcResponseResult for Result<T> {
                     match err {
                         TransportError::ErrorResp(err) => RpcError {
                             code: ErrorCode::from(err.code),
-                            message: err.message.into(),
+                            message: err.message,
                             data: err.data.and_then(|data| serde_json::to_value(data).ok()),
                         },
                         err => RpcError::internal_error_with(format!("Fork Error: {err:?}")),
diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs
index cb1b8508fd24b..72d66b1130558 100644
--- a/crates/anvil/src/eth/fees.rs
+++ b/crates/anvil/src/eth/fees.rs
@@ -24,7 +24,7 @@ use std::{
 pub const MAX_FEE_HISTORY_CACHE_SIZE: u64 = 2048u64;
 
 /// Initial base fee for EIP-1559 blocks.
-pub const INITIAL_BASE_FEE: u128 = 1_000_000_000;
+pub const INITIAL_BASE_FEE: u64 = 1_000_000_000;
 
 /// Initial default gas price for the first block
 pub const INITIAL_GAS_PRICE: u128 = 1_875_000_000;
@@ -47,7 +47,7 @@ pub struct FeeManager {
     /// Tracks the base fee for the next block post London
     ///
     /// This value will be updated after a new block was mined
-    base_fee: Arc<RwLock<u128>>,
+    base_fee: Arc<RwLock<u64>>,
     /// Tracks the excess blob gas, and the base fee, for the next block post Cancun
     ///
     /// This value will be updated after a new block was mined
@@ -62,7 +62,7 @@ pub struct FeeManager {
 impl FeeManager {
     pub fn new(
         spec_id: SpecId,
-        base_fee: u128,
+        base_fee: u64,
         gas_price: u128,
         blob_excess_gas_and_price: BlobExcessGasAndPrice,
     ) -> Self {
@@ -97,7 +97,7 @@ impl FeeManager {
         }
     }
 
-    pub fn base_fee(&self) -> u128 {
+    pub fn base_fee(&self) -> u64 {
         if self.is_eip1559() {
             *self.base_fee.read()
         } else {
@@ -133,7 +133,7 @@ impl FeeManager {
     }
 
     /// Returns the current base fee
-    pub fn set_base_fee(&self, fee: u128) {
+    pub fn set_base_fee(&self, fee: u64) {
         trace!(target: "backend::fees", "updated base fee {:?}", fee);
         let mut base = self.base_fee.write();
         *base = fee;
@@ -151,8 +151,8 @@ impl FeeManager {
         &self,
         gas_used: u128,
         gas_limit: u128,
-        last_fee_per_gas: u128,
-    ) -> u128 {
+        last_fee_per_gas: u64,
+    ) -> u64 {
         // It's naturally impossible for base fee to be 0;
         // It means it was set by the user deliberately and therefore we treat it as a constant.
         // Therefore, we skip the base fee calculation altogether and we return 0.
@@ -179,8 +179,8 @@ impl FeeManager {
 }
 
 /// Calculate base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec
-pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u128) -> u128 {
-    calc_next_block_base_fee(gas_used, gas_limit, base_fee, BaseFeeParams::ethereum())
+pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u64) -> u64 {
+    calc_next_block_base_fee(gas_used as u64, gas_limit as u64, base_fee, BaseFeeParams::ethereum())
 }
 
 /// An async service that takes care of the `FeeHistory` cache
@@ -235,9 +235,9 @@ impl FeeHistoryService {
         };
 
         let mut block_number: Option<u64> = None;
-        let base_fee = header.base_fee_per_gas.unwrap_or_default();
-        let excess_blob_gas = header.excess_blob_gas;
-        let blob_gas_used = header.blob_gas_used;
+        let base_fee = header.base_fee_per_gas.map(|g| g as u128).unwrap_or_default();
+        let excess_blob_gas = header.excess_blob_gas.map(|g| g as u128);
+        let blob_gas_used = header.blob_gas_used.map(|g| g as u128);
         let base_fee_per_blob_gas = header.blob_fee();
         let mut item = FeeHistoryCacheItem {
             base_fee,
diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs
index 31e37c98b7810..74728f94b5931 100644
--- a/crates/anvil/tests/it/anvil_api.rs
+++ b/crates/anvil/tests/it/anvil_api.rs
@@ -48,7 +48,7 @@ async fn can_set_block_gas_limit() {
     // Mine a new block, and check the new block gas limit
     api.mine_one().await;
     let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
-    assert_eq!(block_gas_limit.to::<u128>(), latest_block.header.gas_limit);
+    assert_eq!(block_gas_limit.to::<u64>(), latest_block.header.gas_limit);
 }
 
 // Ref <https://github.com/foundry-rs/foundry/issues/2341>
@@ -557,7 +557,7 @@ async fn test_get_transaction_receipt() {
 
     // the block should have the new base fee
     let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap();
-    assert_eq!(block.header.base_fee_per_gas.unwrap(), new_base_fee.to::<u128>());
+    assert_eq!(block.header.base_fee_per_gas.unwrap(), new_base_fee.to::<u64>());
 
     // mine blocks
     api.evm_mine(None).await.unwrap();
diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs
index f9aaf0dbab029..c4172b265be19 100644
--- a/crates/anvil/tests/it/api.rs
+++ b/crates/anvil/tests/it/api.rs
@@ -5,7 +5,10 @@ use crate::{
     utils::{connect_pubsub_with_wallet, http_provider_with_signer},
 };
 use alloy_network::{EthereumWallet, TransactionBuilder};
-use alloy_primitives::{Address, ChainId, B256, U256};
+use alloy_primitives::{
+    map::{AddressHashMap, B256HashMap, HashMap},
+    Address, ChainId, B256, U256,
+};
 use alloy_provider::Provider;
 use alloy_rpc_types::{
     request::TransactionRequest, state::AccountOverride, BlockId, BlockNumberOrTag,
@@ -14,7 +17,7 @@ use alloy_rpc_types::{
 use alloy_serde::WithOtherFields;
 use anvil::{eth::api::CLIENT_VERSION, spawn, NodeConfig, CHAIN_ID};
 use futures::join;
-use std::{collections::HashMap, time::Duration};
+use std::time::Duration;
 
 #[tokio::test(flavor = "multi_thread")]
 async fn can_get_block_number() {
@@ -174,7 +177,7 @@ async fn can_estimate_gas_with_undersized_max_fee_per_gas() {
     let simple_storage_contract =
         SimpleStorage::deploy(&provider, init_value.clone()).await.unwrap();
 
-    let undersized_max_fee_per_gas = 1_u128;
+    let undersized_max_fee_per_gas = 1;
 
     let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
     let latest_block_base_fee_per_gas = latest_block.header.base_fee_per_gas.unwrap();
@@ -183,7 +186,7 @@ async fn can_estimate_gas_with_undersized_max_fee_per_gas() {
 
     let estimated_gas = simple_storage_contract
         .setValue("new_value".to_string())
-        .max_fee_per_gas(undersized_max_fee_per_gas)
+        .max_fee_per_gas(undersized_max_fee_per_gas.into())
         .from(wallet.address())
         .estimate_gas()
         .await
@@ -255,7 +258,7 @@ async fn can_call_on_pending_block() {
             .call()
             .await
             .unwrap();
-        assert_eq!(block.header.gas_limit, ret_gas_limit.to::<u128>());
+        assert_eq!(block.header.gas_limit, ret_gas_limit.to::<u64>());
 
         let Multicall::getCurrentBlockCoinbaseReturn { coinbase: ret_coinbase, .. } = contract
             .getCurrentBlockCoinbase()
@@ -284,13 +287,13 @@ async fn can_call_with_undersized_max_fee_per_gas() {
 
     let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
     let latest_block_base_fee_per_gas = latest_block.header.base_fee_per_gas.unwrap();
-    let undersized_max_fee_per_gas = 1_u128;
+    let undersized_max_fee_per_gas = 1;
 
     assert!(undersized_max_fee_per_gas < latest_block_base_fee_per_gas);
 
     let last_sender = simple_storage_contract
         .lastSender()
-        .max_fee_per_gas(undersized_max_fee_per_gas)
+        .max_fee_per_gas(undersized_max_fee_per_gas.into())
         .from(wallet.address())
         .call()
         .await
@@ -319,23 +322,24 @@ async fn can_call_with_state_override() {
 
     // Test the `balance` account override
     let balance = U256::from(42u64);
-    let overrides = HashMap::from([(
-        account,
-        AccountOverride { balance: Some(balance), ..Default::default() },
-    )]);
+    let mut overrides = AddressHashMap::default();
+    overrides.insert(account, AccountOverride { balance: Some(balance), ..Default::default() });
     let result =
         multicall_contract.getEthBalance(account).state(overrides).call().await.unwrap().balance;
     assert_eq!(result, balance);
 
     // Test the `state_diff` account override
-    let overrides = HashMap::from([(
+    let mut state_diff = B256HashMap::default();
+    state_diff.insert(B256::ZERO, account.into_word());
+    let mut overrides = AddressHashMap::default();
+    overrides.insert(
         *simple_storage_contract.address(),
         AccountOverride {
             // The `lastSender` is in the first storage slot
-            state_diff: Some(HashMap::from([(B256::ZERO, account.into_word())])),
+            state_diff: Some(state_diff),
             ..Default::default()
         },
-    )]);
+    );
 
     let last_sender =
         simple_storage_contract.lastSender().state(HashMap::default()).call().await.unwrap()._0;
@@ -352,14 +356,17 @@ async fn can_call_with_state_override() {
     assert_eq!(value, init_value);
 
     // Test the `state` account override
-    let overrides = HashMap::from([(
+    let mut state = B256HashMap::default();
+    state.insert(B256::ZERO, account.into_word());
+    let mut overrides = AddressHashMap::default();
+    overrides.insert(
         *simple_storage_contract.address(),
         AccountOverride {
             // The `lastSender` is in the first storage slot
-            state: Some(HashMap::from([(B256::ZERO, account.into_word())])),
+            state: Some(state),
             ..Default::default()
         },
-    )]);
+    );
 
     let last_sender =
         simple_storage_contract.lastSender().state(overrides.clone()).call().await.unwrap()._0;
diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs
index 71a8bfd84c06e..a4243ce15f493 100644
--- a/crates/anvil/tests/it/eip4844.rs
+++ b/crates/anvil/tests/it/eip4844.rs
@@ -138,7 +138,7 @@ async fn can_mine_blobs_when_exceeds_max_blobs() {
     let first_batch = vec![1u8; DATA_GAS_PER_BLOB as usize * 3];
     let sidecar: SidecarBuilder<SimpleCoder> = SidecarBuilder::from_slice(&first_batch);
 
-    let num_blobs_first = sidecar.clone().take().len();
+    let num_blobs_first = sidecar.clone().take().len() as u64;
 
     let sidecar = sidecar.build().unwrap();
 
@@ -160,7 +160,7 @@ async fn can_mine_blobs_when_exceeds_max_blobs() {
 
     let sidecar: SidecarBuilder<SimpleCoder> = SidecarBuilder::from_slice(&second_batch);
 
-    let num_blobs_second = sidecar.clone().take().len();
+    let num_blobs_second = sidecar.clone().take().len() as u64;
 
     let sidecar = sidecar.build().unwrap();
     tx.set_blob_sidecar(sidecar);
@@ -181,12 +181,12 @@ async fn can_mine_blobs_when_exceeds_max_blobs() {
     );
     assert_eq!(
         first_block.unwrap().unwrap().header.blob_gas_used,
-        Some(DATA_GAS_PER_BLOB as u128 * num_blobs_first as u128)
+        Some(DATA_GAS_PER_BLOB * num_blobs_first)
     );
 
     assert_eq!(
         second_block.unwrap().unwrap().header.blob_gas_used,
-        Some(DATA_GAS_PER_BLOB as u128 * num_blobs_second as u128)
+        Some(DATA_GAS_PER_BLOB * num_blobs_second)
     );
     // Mined in two different blocks
     assert_eq!(first_receipt.block_number.unwrap() + 1, second_receipt.block_number.unwrap());
diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs
index f93fa92a19f96..e6db8a0635bbc 100644
--- a/crates/anvil/tests/it/fork.rs
+++ b/crates/anvil/tests/it/fork.rs
@@ -781,7 +781,7 @@ async fn test_fork_can_send_opensea_tx() {
         .value(U256::from(20000000000000000u64))
         .with_input(input)
         .with_gas_price(22180711707u128)
-        .with_gas_limit(150_000u128);
+        .with_gas_limit(150_000);
     let tx = WithOtherFields::new(tx);
 
     let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
@@ -817,7 +817,7 @@ async fn test_fork_init_base_fee() {
     // <https://etherscan.io/block/13184859>
     assert_eq!(block.header.number, 13184859u64);
     let init_base_fee = block.header.base_fee_per_gas.unwrap();
-    assert_eq!(init_base_fee, 63739886069u128);
+    assert_eq!(init_base_fee, 63739886069);
 
     api.mine_one().await;
 
@@ -1185,7 +1185,7 @@ async fn test_fork_reset_basefee() {
     let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
 
     // basefee of +1 block: <https://etherscan.io/block/18835001>
-    assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59455969592u128);
+    assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59455969592u64);
 
     // now reset to block 18835000 -1
     api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(18835000u64 - 1) }))
@@ -1196,7 +1196,7 @@ async fn test_fork_reset_basefee() {
     let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap();
 
     // basefee of the forked block: <https://etherscan.io/block/18835000>
-    assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59017001138u128);
+    assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59017001138);
 }
 
 // <https://github.com/foundry-rs/foundry/issues/6795>
@@ -1288,7 +1288,7 @@ async fn test_base_fork_gas_limit() {
         .unwrap();
 
     assert!(api.gas_limit() >= uint!(132_000_000_U256));
-    assert!(block.header.gas_limit >= 132_000_000_u128);
+    assert!(block.header.gas_limit >= 132_000_000_u64);
 }
 
 // <https://github.com/foundry-rs/foundry/issues/7023>
@@ -1443,7 +1443,7 @@ async fn test_reset_dev_account_nonce() {
                 .from(address)
                 .to(address)
                 .nonce(nonce_after)
-                .gas_limit(21000u128),
+                .gas_limit(21000),
         ))
         .await
         .unwrap()
diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs
index e80a52462875b..55f8321997239 100644
--- a/crates/anvil/tests/it/gas.rs
+++ b/crates/anvil/tests/it/gas.rs
@@ -102,7 +102,7 @@ async fn test_basefee_half_block() {
         .unwrap();
 
     // unchanged, half block
-    assert_eq!(next_base_fee, INITIAL_BASE_FEE);
+    assert_eq!(next_base_fee, { INITIAL_BASE_FEE });
 }
 
 #[tokio::test(flavor = "multi_thread")]
@@ -147,7 +147,7 @@ async fn test_basefee_empty_block() {
 #[tokio::test(flavor = "multi_thread")]
 async fn test_respect_base_fee() {
     let base_fee = 50u128;
-    let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await;
+    let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee as u64))).await;
 
     let provider = handle.http_provider();
 
@@ -168,7 +168,7 @@ async fn test_respect_base_fee() {
 #[tokio::test(flavor = "multi_thread")]
 async fn test_tip_above_fee_cap() {
     let base_fee = 50u128;
-    let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await;
+    let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee as u64))).await;
 
     let provider = handle.http_provider();
 
@@ -190,17 +190,17 @@ async fn test_tip_above_fee_cap() {
 #[tokio::test(flavor = "multi_thread")]
 async fn test_can_use_fee_history() {
     let base_fee = 50u128;
-    let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await;
+    let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee as u64))).await;
     let provider = handle.http_provider();
 
     for _ in 0..10 {
         let fee_history = provider.get_fee_history(1, Default::default(), &[]).await.unwrap();
-        let next_base_fee = fee_history.base_fee_per_gas.last().unwrap();
+        let next_base_fee = *fee_history.base_fee_per_gas.last().unwrap();
 
         let tx = TransactionRequest::default()
             .with_to(Address::random())
             .with_value(U256::from(100))
-            .with_gas_price(*next_base_fee);
+            .with_gas_price(next_base_fee);
         let tx = WithOtherFields::new(tx);
 
         let receipt =
@@ -208,11 +208,11 @@ async fn test_can_use_fee_history() {
         assert!(receipt.inner.inner.is_success());
 
         let fee_history_after = provider.get_fee_history(1, Default::default(), &[]).await.unwrap();
-        let latest_fee_history_fee = fee_history_after.base_fee_per_gas.first().unwrap();
+        let latest_fee_history_fee = *fee_history_after.base_fee_per_gas.first().unwrap() as u64;
         let latest_block =
             provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap();
 
-        assert_eq!(latest_block.header.base_fee_per_gas.unwrap(), *latest_fee_history_fee);
-        assert_eq!(latest_fee_history_fee, next_base_fee);
+        assert_eq!(latest_block.header.base_fee_per_gas.unwrap(), latest_fee_history_fee);
+        assert_eq!(latest_fee_history_fee, next_base_fee as u64);
     }
 }
diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs
index 6446caf9ce682..8de4eab1d1f4f 100644
--- a/crates/anvil/tests/it/optimism.rs
+++ b/crates/anvil/tests/it/optimism.rs
@@ -25,19 +25,21 @@ async fn test_deposits_not_supported_if_optimism_disabled() {
         .with_to(to)
         .with_value(U256::from(1234))
         .with_gas_limit(21000);
-    let tx = WithOtherFields {
-        inner: tx,
-        other: OptimismTransactionFields {
-            source_hash: Some(b256!(
-                "0000000000000000000000000000000000000000000000000000000000000000"
-            )),
-            mint: Some(0),
-            is_system_tx: Some(true),
-            deposit_receipt_version: None,
-        }
-        .into(),
+
+    let op_fields = OptimismTransactionFields {
+        source_hash: Some(b256!(
+            "0000000000000000000000000000000000000000000000000000000000000000"
+        )),
+        mint: Some(0),
+        is_system_tx: Some(true),
+        deposit_receipt_version: None,
     };
 
+    // TODO: Test this
+    let other = serde_json::to_value(op_fields).unwrap().try_into().unwrap();
+
+    let tx = WithOtherFields { inner: tx, other };
+
     let err = provider.send_transaction(tx).await.unwrap_err();
     let s = err.to_string();
     assert!(s.contains("op-stack deposit tx received but is not supported"), "{s:?}");
@@ -61,23 +63,22 @@ async fn test_send_value_deposit_transaction() {
     let send_value = U256::from(1234);
     let before_balance_to = provider.get_balance(to).await.unwrap();
 
+    let op_fields = OptimismTransactionFields {
+        source_hash: Some(b256!(
+            "0000000000000000000000000000000000000000000000000000000000000000"
+        )),
+        mint: Some(0),
+        is_system_tx: Some(true),
+        deposit_receipt_version: None,
+    };
+
+    let other = serde_json::to_value(op_fields).unwrap().try_into().unwrap();
     let tx = TransactionRequest::default()
         .with_from(from)
         .with_to(to)
         .with_value(send_value)
         .with_gas_limit(21000);
-    let tx: WithOtherFields<TransactionRequest> = WithOtherFields {
-        inner: tx,
-        other: OptimismTransactionFields {
-            source_hash: Some(b256!(
-                "0000000000000000000000000000000000000000000000000000000000000000"
-            )),
-            mint: Some(0),
-            is_system_tx: Some(true),
-            deposit_receipt_version: None,
-        }
-        .into(),
-    };
+    let tx: WithOtherFields<TransactionRequest> = WithOtherFields { inner: tx, other };
 
     let pending = provider.send_transaction(tx).await.unwrap().register().await.unwrap();
 
@@ -121,18 +122,17 @@ async fn test_send_value_raw_deposit_transaction() {
         .with_gas_limit(21_000)
         .with_max_fee_per_gas(20_000_000_000)
         .with_max_priority_fee_per_gas(1_000_000_000);
-    let tx = WithOtherFields {
-        inner: tx,
-        other: OptimismTransactionFields {
-            source_hash: Some(b256!(
-                "0000000000000000000000000000000000000000000000000000000000000000"
-            )),
-            mint: Some(0),
-            is_system_tx: Some(true),
-            deposit_receipt_version: None,
-        }
-        .into(),
+
+    let op_fields = OptimismTransactionFields {
+        source_hash: Some(b256!(
+            "0000000000000000000000000000000000000000000000000000000000000000"
+        )),
+        mint: Some(0),
+        is_system_tx: Some(true),
+        deposit_receipt_version: None,
     };
+    let other = serde_json::to_value(op_fields).unwrap().try_into().unwrap();
+    let tx = WithOtherFields { inner: tx, other };
     let tx_envelope = tx.build(&signer).await.unwrap();
     let mut tx_buffer = Vec::with_capacity(tx_envelope.encode_2718_len());
     tx_envelope.encode_2718(&mut tx_buffer);
diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs
index 63ebbbcf3404f..07c120d1ce525 100644
--- a/crates/anvil/tests/it/transaction.rs
+++ b/crates/anvil/tests/it/transaction.rs
@@ -197,7 +197,7 @@ async fn can_reject_too_high_gas_limits() {
     let from = accounts[0].address();
     let to = accounts[1].address();
 
-    let gas_limit = api.gas_limit().to::<u128>();
+    let gas_limit = api.gas_limit().to::<u64>();
     let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap();
 
     let tx =
@@ -237,11 +237,11 @@ async fn can_mine_large_gas_limit() {
     let from = accounts[0].address();
     let to = accounts[1].address();
 
-    let gas_limit = anvil::DEFAULT_GAS_LIMIT;
+    let gas_limit = anvil::DEFAULT_GAS_LIMIT as u64;
     let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap();
 
     let tx =
-        TransactionRequest::default().to(to).value(amount).from(from).with_gas_limit(gas_limit * 3);
+        TransactionRequest::default().to(to).value(amount).from(from).with_gas_limit(gas_limit);
 
     // send transaction with higher gas limit
     let pending = provider.send_transaction(WithOtherFields::new(tx)).await.unwrap();
@@ -580,7 +580,7 @@ async fn can_handle_multiple_concurrent_transfers_with_same_nonce() {
         .value(U256::from(100))
         .from(from)
         .nonce(nonce)
-        .with_gas_limit(21000u128);
+        .with_gas_limit(21000);
 
     let tx = WithOtherFields::new(tx);
 
@@ -621,7 +621,7 @@ async fn can_handle_multiple_concurrent_deploys_with_same_nonce() {
         .from(from)
         .with_input(greeter_calldata.to_owned())
         .nonce(nonce)
-        .with_gas_limit(300_000u128);
+        .with_gas_limit(300_000);
 
     let tx = WithOtherFields::new(tx);
 
@@ -662,7 +662,7 @@ async fn can_handle_multiple_concurrent_transactions_with_same_nonce() {
         .from(from)
         .with_input(deploy_calldata.to_owned())
         .nonce(nonce)
-        .with_gas_limit(300_000u128);
+        .with_gas_limit(300_000);
     let deploy_tx = WithOtherFields::new(deploy_tx);
 
     let set_greeting = greeter_contract.setGreeting("Hello".to_string());
@@ -672,7 +672,7 @@ async fn can_handle_multiple_concurrent_transactions_with_same_nonce() {
         .from(from)
         .with_input(set_greeting_calldata.to_owned())
         .nonce(nonce)
-        .with_gas_limit(300_000u128);
+        .with_gas_limit(300_000);
     let set_greeting_tx = WithOtherFields::new(set_greeting_tx);
 
     for idx in 0..10 {
@@ -1127,7 +1127,7 @@ async fn test_estimate_gas() {
     let addr = recipient;
     let account_override =
         AccountOverride { balance: Some(alloy_primitives::U256::from(1e18)), ..Default::default() };
-    let mut state_override = StateOverride::new();
+    let mut state_override = StateOverride::default();
     state_override.insert(addr, account_override);
 
     // Estimate gas with state override implying sufficient funds.
@@ -1152,7 +1152,7 @@ async fn test_reject_gas_too_low() {
         .to(Address::random())
         .value(U256::from(1337u64))
         .from(account)
-        .with_gas_limit(gas as u128);
+        .with_gas_limit(gas);
     let tx = WithOtherFields::new(tx);
 
     let resp = provider.send_transaction(tx).await;
@@ -1169,7 +1169,7 @@ async fn can_call_with_high_gas_limit() {
 
     let greeter_contract = Greeter::deploy(provider, "Hello World!".to_string()).await.unwrap();
 
-    let greeting = greeter_contract.greet().gas(60_000_000u128).call().await.unwrap();
+    let greeting = greeter_contract.greet().gas(60_000_000).call().await.unwrap();
     assert_eq!("Hello World!", greeting._0);
 }
 
@@ -1179,7 +1179,7 @@ async fn test_reject_eip1559_pre_london() {
         spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Berlin.into()))).await;
     let provider = handle.http_provider();
 
-    let gas_limit = api.gas_limit().to::<u128>();
+    let gas_limit = api.gas_limit().to::<u64>();
     let gas_price = api.gas_price();
 
     let unsupported_call_builder =
diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs
index 8c928b3a8a535..df566d1144c0b 100644
--- a/crates/cast/tests/cli/main.rs
+++ b/crates/cast/tests/cli/main.rs
@@ -54,6 +54,7 @@ mixHash              [..]
 nonce                [..]
 number               [..]
 parentHash           [..]
+parentBeaconRoot     [..]
 transactionsRoot     [..]
 receiptsRoot         [..]
 sha3Uncles           [..]
diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json
index 904627b915453..e6e573b83e8ef 100644
--- a/crates/cheatcodes/assets/cheatcodes.json
+++ b/crates/cheatcodes/assets/cheatcodes.json
@@ -5596,7 +5596,7 @@
     {
       "func": {
         "id": "lastCallGas",
-        "description": "Gets the gas used in the last call.",
+        "description": "Gets the gas used in the last call from the callee perspective.",
         "declaration": "function lastCallGas() external view returns (Gas memory gas);",
         "visibility": "external",
         "mutability": "view",
@@ -8599,6 +8599,46 @@
       },
       "safety": "unsafe"
     },
+    {
+      "func": {
+        "id": "snapshotGasLastCall_0",
+        "description": "Snapshot capture the gas usage of the last call by name from the callee perspective.",
+        "declaration": "function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed);",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "snapshotGasLastCall(string)",
+        "selector": "0xdd9fca12",
+        "selectorBytes": [
+          221,
+          159,
+          202,
+          18
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
+    {
+      "func": {
+        "id": "snapshotGasLastCall_1",
+        "description": "Snapshot capture the gas usage of the last call by name in a group from the callee perspective.",
+        "declaration": "function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed);",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "snapshotGasLastCall(string,string)",
+        "selector": "0x200c6772",
+        "selectorBytes": [
+          32,
+          12,
+          103,
+          114
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
     {
       "func": {
         "id": "snapshotState",
@@ -8619,6 +8659,46 @@
       "status": "stable",
       "safety": "unsafe"
     },
+    {
+      "func": {
+        "id": "snapshotValue_0",
+        "description": "Snapshot capture an arbitrary numerical value by name.\nThe group name is derived from the contract name.",
+        "declaration": "function snapshotValue(string calldata name, uint256 value) external;",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "snapshotValue(string,uint256)",
+        "selector": "0x51db805a",
+        "selectorBytes": [
+          81,
+          219,
+          128,
+          90
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
+    {
+      "func": {
+        "id": "snapshotValue_1",
+        "description": "Snapshot capture an arbitrary numerical value by name in a group.",
+        "declaration": "function snapshotValue(string calldata group, string calldata name, uint256 value) external;",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "snapshotValue(string,string,uint256)",
+        "selector": "0x6d2b27d8",
+        "selectorBytes": [
+          109,
+          43,
+          39,
+          216
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
     {
       "func": {
         "id": "split",
@@ -8779,6 +8859,46 @@
       "status": "stable",
       "safety": "unsafe"
     },
+    {
+      "func": {
+        "id": "startSnapshotGas_0",
+        "description": "Start a snapshot capture of the current gas usage by name.\nThe group name is derived from the contract name.",
+        "declaration": "function startSnapshotGas(string calldata name) external;",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "startSnapshotGas(string)",
+        "selector": "0x3cad9d7b",
+        "selectorBytes": [
+          60,
+          173,
+          157,
+          123
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
+    {
+      "func": {
+        "id": "startSnapshotGas_1",
+        "description": "Start a snapshot capture of the current gas usage by name in a group.",
+        "declaration": "function startSnapshotGas(string calldata group, string calldata name) external;",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "startSnapshotGas(string,string)",
+        "selector": "0x6cd0cc53",
+        "selectorBytes": [
+          108,
+          208,
+          204,
+          83
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
     {
       "func": {
         "id": "startStateDiffRecording",
@@ -8919,6 +9039,66 @@
       "status": "stable",
       "safety": "unsafe"
     },
+    {
+      "func": {
+        "id": "stopSnapshotGas_0",
+        "description": "Stop the snapshot capture of the current gas by latest snapshot name, capturing the gas used since the start.",
+        "declaration": "function stopSnapshotGas() external returns (uint256 gasUsed);",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "stopSnapshotGas()",
+        "selector": "0xf6402eda",
+        "selectorBytes": [
+          246,
+          64,
+          46,
+          218
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
+    {
+      "func": {
+        "id": "stopSnapshotGas_1",
+        "description": "Stop the snapshot capture of the current gas usage by name, capturing the gas used since the start.\nThe group name is derived from the contract name.",
+        "declaration": "function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed);",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "stopSnapshotGas(string)",
+        "selector": "0x773b2805",
+        "selectorBytes": [
+          119,
+          59,
+          40,
+          5
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
+    {
+      "func": {
+        "id": "stopSnapshotGas_2",
+        "description": "Stop the snapshot capture of the current gas usage by name in a group, capturing the gas used since the start.",
+        "declaration": "function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed);",
+        "visibility": "external",
+        "mutability": "",
+        "signature": "stopSnapshotGas(string,string)",
+        "selector": "0x0c9db707",
+        "selectorBytes": [
+          12,
+          157,
+          183,
+          7
+        ]
+      },
+      "group": "evm",
+      "status": "stable",
+      "safety": "unsafe"
+    },
     {
       "func": {
         "id": "store",
diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs
index bff0d1c1e109d..656087d91a38d 100644
--- a/crates/cheatcodes/spec/src/vm.rs
+++ b/crates/cheatcodes/spec/src/vm.rs
@@ -542,6 +542,49 @@ interface Vm {
     #[cheatcode(group = Evm, safety = Unsafe)]
     function readCallers() external returns (CallerMode callerMode, address msgSender, address txOrigin);
 
+    // ----- Arbitrary Snapshots -----
+
+    /// Snapshot capture an arbitrary numerical value by name.
+    /// The group name is derived from the contract name.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function snapshotValue(string calldata name, uint256 value) external;
+
+    /// Snapshot capture an arbitrary numerical value by name in a group.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function snapshotValue(string calldata group, string calldata name, uint256 value) external;
+
+    // -------- Gas Snapshots --------
+
+    /// Snapshot capture the gas usage of the last call by name from the callee perspective.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed);
+
+    /// Snapshot capture the gas usage of the last call by name in a group from the callee perspective.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed);
+
+    /// Start a snapshot capture of the current gas usage by name.
+    /// The group name is derived from the contract name.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function startSnapshotGas(string calldata name) external;
+
+    /// Start a snapshot capture of the current gas usage by name in a group.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function startSnapshotGas(string calldata group, string calldata name) external;
+
+    /// Stop the snapshot capture of the current gas by latest snapshot name, capturing the gas used since the start.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function stopSnapshotGas() external returns (uint256 gasUsed);
+
+    /// Stop the snapshot capture of the current gas usage by name, capturing the gas used since the start.
+    /// The group name is derived from the contract name.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed);
+
+    /// Stop the snapshot capture of the current gas usage by name in a group, capturing the gas used since the start.
+    #[cheatcode(group = Evm, safety = Unsafe)]
+    function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed);
+
     // -------- State Snapshots --------
 
     /// `snapshot` is being deprecated in favor of `snapshotState`. It will be removed in future versions.
@@ -731,7 +774,7 @@ interface Vm {
 
     // -------- Gas Measurement --------
 
-    /// Gets the gas used in the last call.
+    /// Gets the gas used in the last call from the callee perspective.
     #[cheatcode(group = Evm, safety = Safe)]
     function lastCallGas() external view returns (Gas memory gas);
 
diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs
index 531784e16339d..c6a15f45dfd31 100644
--- a/crates/cheatcodes/src/config.rs
+++ b/crates/cheatcodes/src/config.rs
@@ -49,6 +49,8 @@ pub struct CheatsConfig {
     /// If Some, `vm.getDeployedCode` invocations are validated to be in scope of this list.
     /// If None, no validation is performed.
     pub available_artifacts: Option<ContractsByArtifact>,
+    /// Name of the script/test contract which is currently running.
+    pub running_contract: Option<String>,
     /// Version of the script/test contract which is currently running.
     pub running_version: Option<Version>,
     /// Whether to enable legacy (non-reverting) assertions.
@@ -64,6 +66,7 @@ impl CheatsConfig {
         evm_opts: EvmOpts,
         available_artifacts: Option<ContractsByArtifact>,
         script_wallets: Option<ScriptWallets>,
+        running_contract: Option<String>,
         running_version: Option<Version>,
     ) -> Self {
         let mut allowed_paths = vec![config.root.0.clone()];
@@ -92,6 +95,7 @@ impl CheatsConfig {
             labels: config.labels.clone(),
             script_wallets,
             available_artifacts,
+            running_contract,
             running_version,
             assertions_revert: config.assertions_revert,
             seed: config.fuzz.seed,
@@ -221,6 +225,7 @@ impl Default for CheatsConfig {
             labels: Default::default(),
             script_wallets: None,
             available_artifacts: Default::default(),
+            running_contract: Default::default(),
             running_version: Default::default(),
             assertions_revert: true,
             seed: None,
@@ -240,6 +245,7 @@ mod tests {
             None,
             None,
             None,
+            None,
         )
     }
 
diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs
index a201ec577e076..d4ac7172221f4 100644
--- a/crates/cheatcodes/src/evm.rs
+++ b/crates/cheatcodes/src/evm.rs
@@ -1,8 +1,9 @@
 //! Implementations of [`Evm`](spec::Group::Evm) cheatcodes.
 
 use crate::{
-    inspector::RecordDebugStepInfo, BroadcastableTransaction, Cheatcode, Cheatcodes,
-    CheatcodesExecutor, CheatsCtxt, Error, Result, Vm::*,
+    inspector::InnerEcx, inspector::RecordDebugStepInfo, BroadcastableTransaction, Cheatcode, Cheatcodes,
+    CheatcodesExecutor,
+    CheatsCtxt, Error, Result, Vm::*,
 };
 use alloy_consensus::TxEnvelope;
 use alloy_genesis::{Genesis, GenesisAccount};
@@ -16,10 +17,7 @@ use foundry_evm_core::{
 };
 use foundry_evm_traces::StackSnapshotType;
 use rand::Rng;
-use revm::{
-    primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY},
-    InnerEvmContext,
-};
+use revm::primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY};
 use std::{
     collections::{BTreeMap, HashMap},
     path::Path,
@@ -57,6 +55,19 @@ impl RecordAccess {
     }
 }
 
+/// Records the `snapshotGas*` cheatcodes.
+#[derive(Clone, Debug)]
+pub struct GasRecord {
+    /// The group name of the gas snapshot.
+    pub group: String,
+    /// The name of the gas snapshot.
+    pub name: String,
+    /// The total gas used in the gas snapshot.
+    pub gas_used: u64,
+    /// Depth at which the gas snapshot was taken.
+    pub depth: u64,
+}
+
 /// Records `deal` cheatcodes
 #[derive(Clone, Debug)]
 pub struct DealRecord {
@@ -77,21 +88,21 @@ impl Cheatcode for addrCall {
 }
 
 impl Cheatcode for getNonce_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account } = self;
         get_nonce(ccx, account)
     }
 }
 
 impl Cheatcode for getNonce_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { wallet } = self;
         get_nonce(ccx, &wallet.addr)
     }
 }
 
 impl Cheatcode for loadCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { target, slot } = *self;
         ensure_not_precompile!(&target, ccx);
         ccx.ecx.load_account(target)?;
@@ -128,7 +139,7 @@ impl Cheatcode for loadCall {
 }
 
 impl Cheatcode for loadAllocsCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { pathToAllocsJson } = self;
 
         let path = Path::new(pathToAllocsJson);
@@ -154,7 +165,7 @@ impl Cheatcode for loadAllocsCall {
 }
 
 impl Cheatcode for dumpStateCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { pathToStateJson } = self;
         let path = Path::new(pathToStateJson);
 
@@ -274,7 +285,7 @@ impl Cheatcode for lastCallGasCall {
 }
 
 impl Cheatcode for chainIdCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newChainId } = self;
         ensure!(*newChainId <= U256::from(u64::MAX), "chain ID must be less than 2^64 - 1");
         ccx.ecx.env.cfg.chain_id = newChainId.to();
@@ -283,7 +294,7 @@ impl Cheatcode for chainIdCall {
 }
 
 impl Cheatcode for coinbaseCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newCoinbase } = self;
         ccx.ecx.env.block.coinbase = *newCoinbase;
         Ok(Default::default())
@@ -291,7 +302,7 @@ impl Cheatcode for coinbaseCall {
 }
 
 impl Cheatcode for difficultyCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newDifficulty } = self;
         ensure!(
             ccx.ecx.spec_id() < SpecId::MERGE,
@@ -304,7 +315,7 @@ impl Cheatcode for difficultyCall {
 }
 
 impl Cheatcode for feeCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newBasefee } = self;
         ccx.ecx.env.block.basefee = *newBasefee;
         Ok(Default::default())
@@ -312,7 +323,7 @@ impl Cheatcode for feeCall {
 }
 
 impl Cheatcode for prevrandao_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newPrevrandao } = self;
         ensure!(
             ccx.ecx.spec_id() >= SpecId::MERGE,
@@ -325,7 +336,7 @@ impl Cheatcode for prevrandao_0Call {
 }
 
 impl Cheatcode for prevrandao_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newPrevrandao } = self;
         ensure!(
             ccx.ecx.spec_id() >= SpecId::MERGE,
@@ -338,7 +349,7 @@ impl Cheatcode for prevrandao_1Call {
 }
 
 impl Cheatcode for blobhashesCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { hashes } = self;
         ensure!(
             ccx.ecx.spec_id() >= SpecId::CANCUN,
@@ -351,7 +362,7 @@ impl Cheatcode for blobhashesCall {
 }
 
 impl Cheatcode for getBlobhashesCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         ensure!(
             ccx.ecx.spec_id() >= SpecId::CANCUN,
@@ -363,7 +374,7 @@ impl Cheatcode for getBlobhashesCall {
 }
 
 impl Cheatcode for rollCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newHeight } = self;
         ccx.ecx.env.block.number = *newHeight;
         Ok(Default::default())
@@ -371,14 +382,14 @@ impl Cheatcode for rollCall {
 }
 
 impl Cheatcode for getBlockNumberCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         Ok(ccx.ecx.env.block.number.abi_encode())
     }
 }
 
 impl Cheatcode for txGasPriceCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newGasPrice } = self;
         ccx.ecx.env.tx.gas_price = *newGasPrice;
         Ok(Default::default())
@@ -386,7 +397,7 @@ impl Cheatcode for txGasPriceCall {
 }
 
 impl Cheatcode for warpCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newTimestamp } = self;
         ccx.ecx.env.block.timestamp = *newTimestamp;
         Ok(Default::default())
@@ -394,14 +405,14 @@ impl Cheatcode for warpCall {
 }
 
 impl Cheatcode for getBlockTimestampCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         Ok(ccx.ecx.env.block.timestamp.abi_encode())
     }
 }
 
 impl Cheatcode for blobBaseFeeCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { newBlobBaseFee } = self;
         ensure!(
             ccx.ecx.spec_id() >= SpecId::CANCUN,
@@ -414,14 +425,14 @@ impl Cheatcode for blobBaseFeeCall {
 }
 
 impl Cheatcode for getBlobBaseFeeCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         Ok(ccx.ecx.env.block.get_blob_excess_gas().unwrap_or(0).abi_encode())
     }
 }
 
 impl Cheatcode for dealCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account: address, newBalance: new_balance } = *self;
         let account = journaled_account(ccx.ecx, address)?;
         let old_balance = std::mem::replace(&mut account.info.balance, new_balance);
@@ -432,7 +443,7 @@ impl Cheatcode for dealCall {
 }
 
 impl Cheatcode for etchCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { target, newRuntimeBytecode } = self;
         ensure_not_precompile!(target, ccx);
         ccx.ecx.load_account(*target)?;
@@ -443,7 +454,7 @@ impl Cheatcode for etchCall {
 }
 
 impl Cheatcode for resetNonceCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account } = self;
         let account = journaled_account(ccx.ecx, *account)?;
         // Per EIP-161, EOA nonces start at 0, but contract nonces
@@ -458,7 +469,7 @@ impl Cheatcode for resetNonceCall {
 }
 
 impl Cheatcode for setNonceCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account, newNonce } = *self;
         let account = journaled_account(ccx.ecx, account)?;
         // nonce must increment only
@@ -474,7 +485,7 @@ impl Cheatcode for setNonceCall {
 }
 
 impl Cheatcode for setNonceUnsafeCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account, newNonce } = *self;
         let account = journaled_account(ccx.ecx, account)?;
         account.info.nonce = newNonce;
@@ -483,7 +494,7 @@ impl Cheatcode for setNonceUnsafeCall {
 }
 
 impl Cheatcode for storeCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { target, slot, value } = *self;
         ensure_not_precompile!(&target, ccx);
         // ensure the account is touched
@@ -494,7 +505,7 @@ impl Cheatcode for storeCall {
 }
 
 impl Cheatcode for coolCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { target } = self;
         if let Some(account) = ccx.ecx.journaled_state.state.get_mut(target) {
             account.unmark_touch();
@@ -505,22 +516,96 @@ impl Cheatcode for coolCall {
 }
 
 impl Cheatcode for readCallersCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         read_callers(ccx.state, &ccx.ecx.env.tx.caller)
     }
 }
 
+impl Cheatcode for snapshotValue_0Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { name, value } = self;
+        inner_value_snapshot(ccx, None, Some(name.clone()), value.to_string())
+    }
+}
+
+impl Cheatcode for snapshotValue_1Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { group, name, value } = self;
+        inner_value_snapshot(ccx, Some(group.clone()), Some(name.clone()), value.to_string())
+    }
+}
+
+impl Cheatcode for snapshotGasLastCall_0Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { name } = self;
+        let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else {
+            bail!("no external call was made yet");
+        };
+        inner_last_gas_snapshot(ccx, None, Some(name.clone()), last_call_gas.gasTotalUsed)
+    }
+}
+
+impl Cheatcode for snapshotGasLastCall_1Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { name, group } = self;
+        let Some(last_call_gas) = &ccx.state.gas_metering.last_call_gas else {
+            bail!("no external call was made yet");
+        };
+        inner_last_gas_snapshot(
+            ccx,
+            Some(group.clone()),
+            Some(name.clone()),
+            last_call_gas.gasTotalUsed,
+        )
+    }
+}
+
+impl Cheatcode for startSnapshotGas_0Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { name } = self;
+        inner_start_gas_snapshot(ccx, None, Some(name.clone()))
+    }
+}
+
+impl Cheatcode for startSnapshotGas_1Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { group, name } = self;
+        inner_start_gas_snapshot(ccx, Some(group.clone()), Some(name.clone()))
+    }
+}
+
+impl Cheatcode for stopSnapshotGas_0Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self {} = self;
+        inner_stop_gas_snapshot(ccx, None, None)
+    }
+}
+
+impl Cheatcode for stopSnapshotGas_1Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { name } = self;
+        inner_stop_gas_snapshot(ccx, None, Some(name.clone()))
+    }
+}
+
+impl Cheatcode for stopSnapshotGas_2Call {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
+        let Self { group, name } = self;
+        inner_stop_gas_snapshot(ccx, Some(group.clone()), Some(name.clone()))
+    }
+}
+
 // Deprecated in favor of `snapshotStateCall`
 impl Cheatcode for snapshotCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         inner_snapshot_state(ccx)
     }
 }
 
 impl Cheatcode for snapshotStateCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         inner_snapshot_state(ccx)
     }
@@ -528,14 +613,14 @@ impl Cheatcode for snapshotStateCall {
 
 // Deprecated in favor of `revertToStateCall`
 impl Cheatcode for revertToCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { snapshotId } = self;
         inner_revert_to_state(ccx, *snapshotId)
     }
 }
 
 impl Cheatcode for revertToStateCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { snapshotId } = self;
         inner_revert_to_state(ccx, *snapshotId)
     }
@@ -543,14 +628,14 @@ impl Cheatcode for revertToStateCall {
 
 // Deprecated in favor of `revertToStateAndDeleteCall`
 impl Cheatcode for revertToAndDeleteCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { snapshotId } = self;
         inner_revert_to_state_and_delete(ccx, *snapshotId)
     }
 }
 
 impl Cheatcode for revertToStateAndDeleteCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { snapshotId } = self;
         inner_revert_to_state_and_delete(ccx, *snapshotId)
     }
@@ -558,14 +643,14 @@ impl Cheatcode for revertToStateAndDeleteCall {
 
 // Deprecated in favor of `deleteStateSnapshotCall`
 impl Cheatcode for deleteSnapshotCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { snapshotId } = self;
         inner_delete_state_snapshot(ccx, *snapshotId)
     }
 }
 
 impl Cheatcode for deleteStateSnapshotCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { snapshotId } = self;
         inner_delete_state_snapshot(ccx, *snapshotId)
     }
@@ -573,14 +658,14 @@ impl Cheatcode for deleteStateSnapshotCall {
 
 // Deprecated in favor of `deleteStateSnapshotsCall`
 impl Cheatcode for deleteSnapshotsCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         inner_delete_state_snapshots(ccx)
     }
 }
 
 impl Cheatcode for deleteStateSnapshotsCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         inner_delete_state_snapshots(ccx)
     }
@@ -602,11 +687,7 @@ impl Cheatcode for stopAndReturnStateDiffCall {
 }
 
 impl Cheatcode for broadcastRawTransactionCall {
-    fn apply_full<DB: DatabaseExt, E: CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let mut data = self.data.as_ref();
         let tx = TxEnvelope::decode(&mut data)
             .map_err(|err| fmt_err!("failed to decode RLP-encoded transaction: {err}"))?;
@@ -615,7 +696,7 @@ impl Cheatcode for broadcastRawTransactionCall {
             tx.clone().into(),
             &ccx.ecx.env,
             &mut ccx.ecx.journaled_state,
-            &mut executor.get_inspector(ccx.state),
+            &mut *executor.get_inspector(ccx.state),
         )?;
 
         if ccx.state.broadcast.is_some() {
@@ -630,7 +711,7 @@ impl Cheatcode for broadcastRawTransactionCall {
 }
 
 impl Cheatcode for setBlockhashCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { blockNumber, blockHash } = *self;
         ensure!(
             blockNumber <= ccx.ecx.env.block.number,
@@ -644,11 +725,7 @@ impl Cheatcode for setBlockhashCall {
 }
 
 impl Cheatcode for startDebugTraceRecordingCall {
-    fn apply_full<DB: DatabaseExt, E: CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_mut()) else {
             return Err(Error::from("no tracer initiated, consider adding -vvv flag"))
         };
@@ -679,11 +756,7 @@ impl Cheatcode for startDebugTraceRecordingCall {
 }
 
 impl Cheatcode for stopAndReturnDebugTraceRecordingCall {
-    fn apply_full<DB: DatabaseExt, E: CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_mut()) else {
             return Err(Error::from("no tracer initiated, consider adding -vvv flag"))
         };
@@ -709,16 +782,16 @@ impl Cheatcode for stopAndReturnDebugTraceRecordingCall {
     }
 }
 
-pub(super) fn get_nonce<DB: DatabaseExt>(ccx: &mut CheatsCtxt<DB>, address: &Address) -> Result {
+pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result {
     let account = ccx.ecx.journaled_state.load_account(*address, &mut ccx.ecx.db)?;
     Ok(account.info.nonce.abi_encode())
 }
 
-fn inner_snapshot_state<DB: DatabaseExt>(ccx: &mut CheatsCtxt<DB>) -> Result {
+fn inner_snapshot_state(ccx: &mut CheatsCtxt) -> Result {
     Ok(ccx.ecx.db.snapshot_state(&ccx.ecx.journaled_state, &ccx.ecx.env).abi_encode())
 }
 
-fn inner_revert_to_state<DB: DatabaseExt>(ccx: &mut CheatsCtxt<DB>, snapshot_id: U256) -> Result {
+fn inner_revert_to_state(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result {
     let result = if let Some(journaled_state) = ccx.ecx.db.revert_state(
         snapshot_id,
         &ccx.ecx.journaled_state,
@@ -734,10 +807,7 @@ fn inner_revert_to_state<DB: DatabaseExt>(ccx: &mut CheatsCtxt<DB>, snapshot_id:
     Ok(result.abi_encode())
 }
 
-fn inner_revert_to_state_and_delete<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
-    snapshot_id: U256,
-) -> Result {
+fn inner_revert_to_state_and_delete(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result {
     let result = if let Some(journaled_state) = ccx.ecx.db.revert_state(
         snapshot_id,
         &ccx.ecx.journaled_state,
@@ -753,19 +823,132 @@ fn inner_revert_to_state_and_delete<DB: DatabaseExt>(
     Ok(result.abi_encode())
 }
 
-fn inner_delete_state_snapshot<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
-    snapshot_id: U256,
-) -> Result {
+fn inner_delete_state_snapshot(ccx: &mut CheatsCtxt, snapshot_id: U256) -> Result {
     let result = ccx.ecx.db.delete_state_snapshot(snapshot_id);
     Ok(result.abi_encode())
 }
 
-fn inner_delete_state_snapshots<DB: DatabaseExt>(ccx: &mut CheatsCtxt<DB>) -> Result {
+fn inner_delete_state_snapshots(ccx: &mut CheatsCtxt) -> Result {
     ccx.ecx.db.delete_state_snapshots();
     Ok(Default::default())
 }
 
+fn inner_value_snapshot(
+    ccx: &mut CheatsCtxt,
+    group: Option<String>,
+    name: Option<String>,
+    value: String,
+) -> Result {
+    let (group, name) = derive_snapshot_name(ccx, group, name);
+
+    ccx.state.gas_snapshots.entry(group).or_default().insert(name, value);
+
+    Ok(Default::default())
+}
+
+fn inner_last_gas_snapshot(
+    ccx: &mut CheatsCtxt,
+    group: Option<String>,
+    name: Option<String>,
+    value: u64,
+) -> Result {
+    let (group, name) = derive_snapshot_name(ccx, group, name);
+
+    ccx.state.gas_snapshots.entry(group).or_default().insert(name, value.to_string());
+
+    Ok(value.abi_encode())
+}
+
+fn inner_start_gas_snapshot(
+    ccx: &mut CheatsCtxt,
+    group: Option<String>,
+    name: Option<String>,
+) -> Result {
+    // Revert if there is an active gas snapshot as we can only have one active snapshot at a time.
+    if ccx.state.gas_metering.active_gas_snapshot.is_some() {
+        let (group, name) = ccx.state.gas_metering.active_gas_snapshot.as_ref().unwrap().clone();
+        bail!("gas snapshot was already started with group: {group} and name: {name}");
+    }
+
+    let (group, name) = derive_snapshot_name(ccx, group, name);
+
+    ccx.state.gas_metering.gas_records.push(GasRecord {
+        group: group.clone(),
+        name: name.clone(),
+        gas_used: 0,
+        depth: ccx.ecx.journaled_state.depth(),
+    });
+
+    ccx.state.gas_metering.active_gas_snapshot = Some((group, name));
+
+    ccx.state.gas_metering.start();
+
+    Ok(Default::default())
+}
+
+fn inner_stop_gas_snapshot(
+    ccx: &mut CheatsCtxt,
+    group: Option<String>,
+    name: Option<String>,
+) -> Result {
+    // If group and name are not provided, use the last snapshot group and name.
+    let (group, name) = group.zip(name).unwrap_or_else(|| {
+        let (group, name) = ccx.state.gas_metering.active_gas_snapshot.as_ref().unwrap().clone();
+        (group, name)
+    });
+
+    if let Some(record) = ccx
+        .state
+        .gas_metering
+        .gas_records
+        .iter_mut()
+        .find(|record| record.group == group && record.name == name)
+    {
+        // Calculate the gas used since the snapshot was started.
+        // We subtract 171 from the gas used to account for gas used by the snapshot itself.
+        let value = record.gas_used.saturating_sub(171);
+
+        ccx.state
+            .gas_snapshots
+            .entry(group.clone())
+            .or_default()
+            .insert(name.clone(), value.to_string());
+
+        // Stop the gas metering.
+        ccx.state.gas_metering.stop();
+
+        // Remove the gas record.
+        ccx.state
+            .gas_metering
+            .gas_records
+            .retain(|record| record.group != group && record.name != name);
+
+        // Clear last snapshot cache if we have an exact match.
+        if let Some((snapshot_group, snapshot_name)) = &ccx.state.gas_metering.active_gas_snapshot {
+            if snapshot_group == &group && snapshot_name == &name {
+                ccx.state.gas_metering.active_gas_snapshot = None;
+            }
+        }
+
+        Ok(value.abi_encode())
+    } else {
+        bail!("no gas snapshot was started with the name: {name} in group: {group}");
+    }
+}
+
+// Derives the snapshot group and name from the provided group and name or the running contract.
+fn derive_snapshot_name(
+    ccx: &CheatsCtxt,
+    group: Option<String>,
+    name: Option<String>,
+) -> (String, String) {
+    let group = group.unwrap_or_else(|| {
+        ccx.state.config.running_contract.clone().expect("expected running contract")
+    });
+    let name = name.unwrap_or_else(|| "default".to_string());
+    (group, name)
+}
+
 /// Reads the current caller information and returns the current [CallerMode], `msg.sender` and
 /// `tx.origin`.
 ///
@@ -815,10 +998,10 @@ fn read_callers(state: &Cheatcodes, default_sender: &Address) -> Result {
 }
 
 /// Ensures the `Account` is loaded and touched.
-pub(super) fn journaled_account<DB: DatabaseExt>(
-    ecx: &mut InnerEvmContext<DB>,
+pub(super) fn journaled_account<'a>(
+    ecx: InnerEcx<'a, '_, '_>,
     addr: Address,
-) -> Result<&mut Account> {
+) -> Result<&'a mut Account> {
     ecx.load_account(addr)?;
     ecx.journaled_state.touch(&addr);
     Ok(ecx.journaled_state.state.get_mut(&addr).expect("account is loaded"))
diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs
index a8cc830009fec..84fda7883c7d5 100644
--- a/crates/cheatcodes/src/evm/fork.rs
+++ b/crates/cheatcodes/src/evm/fork.rs
@@ -1,4 +1,4 @@
-use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*};
+use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, DatabaseExt, Result, Vm::*};
 use alloy_dyn_abi::DynSolValue;
 use alloy_primitives::{B256, U256};
 use alloy_provider::Provider;
@@ -8,7 +8,7 @@ use foundry_common::provider::ProviderBuilder;
 use foundry_evm_core::fork::CreateFork;
 
 impl Cheatcode for activeForkCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         ccx.ecx
             .db
@@ -19,49 +19,49 @@ impl Cheatcode for activeForkCall {
 }
 
 impl Cheatcode for createFork_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { urlOrAlias } = self;
         create_fork(ccx, urlOrAlias, None)
     }
 }
 
 impl Cheatcode for createFork_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { urlOrAlias, blockNumber } = self;
         create_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to()))
     }
 }
 
 impl Cheatcode for createFork_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { urlOrAlias, txHash } = self;
         create_fork_at_transaction(ccx, urlOrAlias, txHash)
     }
 }
 
 impl Cheatcode for createSelectFork_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { urlOrAlias } = self;
         create_select_fork(ccx, urlOrAlias, None)
     }
 }
 
 impl Cheatcode for createSelectFork_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { urlOrAlias, blockNumber } = self;
         create_select_fork(ccx, urlOrAlias, Some(blockNumber.saturating_to()))
     }
 }
 
 impl Cheatcode for createSelectFork_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { urlOrAlias, txHash } = self;
         create_select_fork_at_transaction(ccx, urlOrAlias, txHash)
     }
 }
 
 impl Cheatcode for rollFork_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { blockNumber } = self;
         persist_caller(ccx);
         ccx.ecx.db.roll_fork(
@@ -75,7 +75,7 @@ impl Cheatcode for rollFork_0Call {
 }
 
 impl Cheatcode for rollFork_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { txHash } = self;
         persist_caller(ccx);
         ccx.ecx.db.roll_fork_to_transaction(
@@ -89,7 +89,7 @@ impl Cheatcode for rollFork_1Call {
 }
 
 impl Cheatcode for rollFork_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { forkId, blockNumber } = self;
         persist_caller(ccx);
         ccx.ecx.db.roll_fork(
@@ -103,7 +103,7 @@ impl Cheatcode for rollFork_2Call {
 }
 
 impl Cheatcode for rollFork_3Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { forkId, txHash } = self;
         persist_caller(ccx);
         ccx.ecx.db.roll_fork_to_transaction(
@@ -117,7 +117,7 @@ impl Cheatcode for rollFork_3Call {
 }
 
 impl Cheatcode for selectForkCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { forkId } = self;
         persist_caller(ccx);
         check_broadcast(ccx.state)?;
@@ -128,11 +128,7 @@ impl Cheatcode for selectForkCall {
 }
 
 impl Cheatcode for transact_0Call {
-    fn apply_full<DB: DatabaseExt, E: crate::CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let Self { txHash } = *self;
         ccx.ecx.db.transact(
             None,
@@ -146,25 +142,21 @@ impl Cheatcode for transact_0Call {
 }
 
 impl Cheatcode for transact_1Call {
-    fn apply_full<DB: DatabaseExt, E: crate::CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let Self { forkId, txHash } = *self;
         ccx.ecx.db.transact(
             Some(forkId),
             txHash,
             &mut ccx.ecx.env,
             &mut ccx.ecx.journaled_state,
-            &mut executor.get_inspector(ccx.state),
+            &mut *executor.get_inspector(ccx.state),
         )?;
         Ok(Default::default())
     }
 }
 
 impl Cheatcode for allowCheatcodesCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account } = self;
         ccx.ecx.db.allow_cheatcode_access(*account);
         Ok(Default::default())
@@ -172,7 +164,7 @@ impl Cheatcode for allowCheatcodesCall {
 }
 
 impl Cheatcode for makePersistent_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account } = self;
         ccx.ecx.db.add_persistent_account(*account);
         Ok(Default::default())
@@ -180,7 +172,7 @@ impl Cheatcode for makePersistent_0Call {
 }
 
 impl Cheatcode for makePersistent_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account0, account1 } = self;
         ccx.ecx.db.add_persistent_account(*account0);
         ccx.ecx.db.add_persistent_account(*account1);
@@ -189,7 +181,7 @@ impl Cheatcode for makePersistent_1Call {
 }
 
 impl Cheatcode for makePersistent_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account0, account1, account2 } = self;
         ccx.ecx.db.add_persistent_account(*account0);
         ccx.ecx.db.add_persistent_account(*account1);
@@ -199,7 +191,7 @@ impl Cheatcode for makePersistent_2Call {
 }
 
 impl Cheatcode for makePersistent_3Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { accounts } = self;
         for account in accounts {
             ccx.ecx.db.add_persistent_account(*account);
@@ -209,7 +201,7 @@ impl Cheatcode for makePersistent_3Call {
 }
 
 impl Cheatcode for revokePersistent_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account } = self;
         ccx.ecx.db.remove_persistent_account(account);
         Ok(Default::default())
@@ -217,7 +209,7 @@ impl Cheatcode for revokePersistent_0Call {
 }
 
 impl Cheatcode for revokePersistent_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { accounts } = self;
         for account in accounts {
             ccx.ecx.db.remove_persistent_account(account);
@@ -227,14 +219,14 @@ impl Cheatcode for revokePersistent_1Call {
 }
 
 impl Cheatcode for isPersistentCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { account } = self;
         Ok(ccx.ecx.db.is_persistent(account).abi_encode())
     }
 }
 
 impl Cheatcode for rpc_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { method, params } = self;
         let url =
             ccx.ecx.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?;
@@ -251,7 +243,7 @@ impl Cheatcode for rpc_1Call {
 }
 
 impl Cheatcode for eth_getLogsCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { fromBlock, toBlock, target, topics } = self;
         let (Ok(from_block), Ok(to_block)) = (u64::try_from(fromBlock), u64::try_from(toBlock))
         else {
@@ -293,11 +285,7 @@ impl Cheatcode for eth_getLogsCall {
 }
 
 /// Creates and then also selects the new fork
-fn create_select_fork<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
-    url_or_alias: &str,
-    block: Option<u64>,
-) -> Result {
+fn create_select_fork(ccx: &mut CheatsCtxt, url_or_alias: &str, block: Option<u64>) -> Result {
     check_broadcast(ccx.state)?;
 
     let fork = create_fork_request(ccx, url_or_alias, block)?;
@@ -306,19 +294,15 @@ fn create_select_fork<DB: DatabaseExt>(
 }
 
 /// Creates a new fork
-fn create_fork<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
-    url_or_alias: &str,
-    block: Option<u64>,
-) -> Result {
+fn create_fork(ccx: &mut CheatsCtxt, url_or_alias: &str, block: Option<u64>) -> Result {
     let fork = create_fork_request(ccx, url_or_alias, block)?;
     let id = ccx.ecx.db.create_fork(fork)?;
     Ok(id.abi_encode())
 }
 
 /// Creates and then also selects the new fork at the given transaction
-fn create_select_fork_at_transaction<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
+fn create_select_fork_at_transaction(
+    ccx: &mut CheatsCtxt,
     url_or_alias: &str,
     transaction: &B256,
 ) -> Result {
@@ -335,8 +319,8 @@ fn create_select_fork_at_transaction<DB: DatabaseExt>(
 }
 
 /// Creates a new fork at the given transaction
-fn create_fork_at_transaction<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
+fn create_fork_at_transaction(
+    ccx: &mut CheatsCtxt,
     url_or_alias: &str,
     transaction: &B256,
 ) -> Result {
@@ -346,8 +330,8 @@ fn create_fork_at_transaction<DB: DatabaseExt>(
 }
 
 /// Creates the request object for a new fork request
-fn create_fork_request<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
+fn create_fork_request(
+    ccx: &mut CheatsCtxt,
     url_or_alias: &str,
     block: Option<u64>,
 ) -> Result<CreateFork> {
@@ -380,7 +364,7 @@ fn check_broadcast(state: &Cheatcodes) -> Result<()> {
 // Applies to create, select and roll forks actions.
 // https://github.com/foundry-rs/foundry/issues/8004
 #[inline]
-fn persist_caller<DB: DatabaseExt>(ccx: &mut CheatsCtxt<DB>) {
+fn persist_caller(ccx: &mut CheatsCtxt) {
     ccx.ecx.db.add_persistent_account(ccx.caller);
 }
 
diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs
index 01a02c5c21c9d..ab858c612da69 100644
--- a/crates/cheatcodes/src/evm/mock.rs
+++ b/crates/cheatcodes/src/evm/mock.rs
@@ -1,6 +1,6 @@
-use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*};
+use crate::{inspector::InnerEcx, Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*};
 use alloy_primitives::{Address, Bytes, U256};
-use revm::{interpreter::InstructionResult, primitives::Bytecode, InnerEvmContext};
+use revm::{interpreter::InstructionResult, primitives::Bytecode};
 use std::cmp::Ordering;
 
 /// Mocked call data.
@@ -47,7 +47,7 @@ impl Cheatcode for clearMockedCallsCall {
 }
 
 impl Cheatcode for mockCall_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { callee, data, returnData } = self;
         let _ = make_acc_non_empty(callee, ccx.ecx)?;
 
@@ -57,7 +57,7 @@ impl Cheatcode for mockCall_0Call {
 }
 
 impl Cheatcode for mockCall_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { callee, msgValue, data, returnData } = self;
         ccx.ecx.load_account(*callee)?;
         mock_call(ccx.state, callee, data, Some(msgValue), returnData, InstructionResult::Return);
@@ -66,7 +66,7 @@ impl Cheatcode for mockCall_1Call {
 }
 
 impl Cheatcode for mockCallRevert_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { callee, data, revertData } = self;
         let _ = make_acc_non_empty(callee, ccx.ecx)?;
 
@@ -76,7 +76,7 @@ impl Cheatcode for mockCallRevert_0Call {
 }
 
 impl Cheatcode for mockCallRevert_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { callee, msgValue, data, revertData } = self;
         let _ = make_acc_non_empty(callee, ccx.ecx)?;
 
@@ -111,7 +111,7 @@ fn mock_call(
 
 // Etches a single byte onto the account if it is empty to circumvent the `extcodesize`
 // check Solidity might perform.
-fn make_acc_non_empty<DB: DatabaseExt>(callee: &Address, ecx: &mut InnerEvmContext<DB>) -> Result {
+fn make_acc_non_empty(callee: &Address, ecx: InnerEcx) -> Result {
     let acc = ecx.load_account(*callee)?;
 
     let empty_bytecode = acc.info.code.as_ref().map_or(true, Bytecode::is_empty);
diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs
index fe5418b3157f8..a310e28e515bc 100644
--- a/crates/cheatcodes/src/evm/prank.rs
+++ b/crates/cheatcodes/src/evm/prank.rs
@@ -1,4 +1,4 @@
-use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*};
+use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*};
 use alloy_primitives::Address;
 
 /// Prank information.
@@ -45,28 +45,28 @@ impl Prank {
 }
 
 impl Cheatcode for prank_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { msgSender } = self;
         prank(ccx, msgSender, None, true)
     }
 }
 
 impl Cheatcode for startPrank_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { msgSender } = self;
         prank(ccx, msgSender, None, false)
     }
 }
 
 impl Cheatcode for prank_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { msgSender, txOrigin } = self;
         prank(ccx, msgSender, Some(txOrigin), true)
     }
 }
 
 impl Cheatcode for startPrank_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { msgSender, txOrigin } = self;
         prank(ccx, msgSender, Some(txOrigin), false)
     }
@@ -80,8 +80,8 @@ impl Cheatcode for stopPrankCall {
     }
 }
 
-fn prank<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
+fn prank(
+    ccx: &mut CheatsCtxt,
     new_caller: &Address,
     new_origin: Option<&Address>,
     single_call: bool,
diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs
index 4185b2d799976..f36c8d6fe8f8c 100644
--- a/crates/cheatcodes/src/fs.rs
+++ b/crates/cheatcodes/src/fs.rs
@@ -9,7 +9,6 @@ use alloy_sol_types::SolValue;
 use dialoguer::{Input, Password};
 use foundry_common::fs;
 use foundry_config::fs_permissions::FsAccessKind;
-use foundry_evm_core::backend::DatabaseExt;
 use revm::interpreter::CreateInputs;
 use semver::Version;
 use std::{
@@ -293,11 +292,7 @@ impl Cheatcode for getDeployedCodeCall {
 }
 
 impl Cheatcode for deployCode_0Call {
-    fn apply_full<DB: DatabaseExt, E: CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let Self { artifactPath: path } = self;
         let bytecode = get_artifact_code(ccx.state, path, false)?;
         let address = executor
@@ -319,11 +314,7 @@ impl Cheatcode for deployCode_0Call {
 }
 
 impl Cheatcode for deployCode_1Call {
-    fn apply_full<DB: DatabaseExt, E: CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let Self { artifactPath: path, constructorArgs } = self;
         let mut bytecode = get_artifact_code(ccx.state, path, false)?.to_vec();
         bytecode.extend_from_slice(constructorArgs);
diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs
index 5ad5a7f74e439..8c79348f4d7aa 100644
--- a/crates/cheatcodes/src/inspector.rs
+++ b/crates/cheatcodes/src/inspector.rs
@@ -5,7 +5,7 @@ use crate::{
         mapping::{self, MappingSlots},
         mock::{MockCallDataContext, MockCallReturnData},
         prank::Prank,
-        DealRecord, RecordAccess,
+        DealRecord, GasRecord, RecordAccess,
     },
     inspector::utils::CommonCreateInput,
     script::{Broadcast, ScriptWallets},
@@ -17,8 +17,8 @@ use crate::{
         },
     },
     utils::IgnoredTraces,
-    CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, Vm,
-    Vm::AccountAccess,
+    CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
+    Vm::{self, AccountAccess},
 };
 use alloy_primitives::{
     hex,
@@ -31,7 +31,7 @@ use foundry_common::{evm::Breakpoints, TransactionMaybeSigned, SELECTOR_LEN};
 use foundry_config::Config;
 use foundry_evm_core::{
     abi::Vm::stopExpectSafeMemoryCall,
-    backend::{DatabaseExt, RevertDiagnostic},
+    backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
     constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
     utils::new_evm_with_existing_context,
     InspectorExt,
@@ -62,6 +62,9 @@ use std::{
 
 mod utils;
 
+pub type Ecx<'a, 'b, 'c> = &'a mut EvmContext<&'b mut (dyn DatabaseExt + 'c)>;
+pub type InnerEcx<'a, 'b, 'c> = &'a mut InnerEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
+
 /// Helper trait for obtaining complete [revm::Inspector] instance from mutable reference to
 /// [Cheatcodes].
 ///
@@ -70,71 +73,30 @@ mod utils;
 pub trait CheatcodesExecutor {
     /// Core trait method accepting mutable reference to [Cheatcodes] and returning
     /// [revm::Inspector].
-    fn get_inspector<'a, DB: DatabaseExt>(
-        &'a mut self,
-        cheats: &'a mut Cheatcodes,
-    ) -> impl InspectorExt<DB> + 'a;
-
-    /// Constructs [revm::Evm] and runs a given closure with it.
-    fn with_evm<DB: DatabaseExt, F, O>(
-        &mut self,
-        ccx: &mut CheatsCtxt<DB>,
-        f: F,
-    ) -> Result<O, EVMError<DB::Error>>
-    where
-        F: for<'a, 'b> FnOnce(
-            &mut revm::Evm<
-                '_,
-                &'b mut dyn InspectorExt<&'a mut dyn DatabaseExt>,
-                &'a mut dyn DatabaseExt,
-            >,
-        ) -> Result<O, EVMError<DB::Error>>,
-    {
-        let mut inspector = self.get_inspector(ccx.state);
-        let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
-        let l1_block_info = std::mem::take(&mut ccx.ecx.l1_block_info);
-
-        let inner = revm::InnerEvmContext {
-            env: ccx.ecx.env.clone(),
-            journaled_state: std::mem::replace(
-                &mut ccx.ecx.journaled_state,
-                revm::JournaledState::new(Default::default(), Default::default()),
-            ),
-            db: &mut ccx.ecx.db as &mut dyn DatabaseExt,
-            error,
-            l1_block_info,
-        };
-
-        let mut evm = new_evm_with_existing_context(inner, &mut inspector as _);
-
-        let res = f(&mut evm)?;
-
-        ccx.ecx.journaled_state = evm.context.evm.inner.journaled_state;
-        ccx.ecx.env = evm.context.evm.inner.env;
-        ccx.ecx.l1_block_info = evm.context.evm.inner.l1_block_info;
-        ccx.ecx.error = evm.context.evm.inner.error;
-
-        Ok(res)
-    }
+    fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
 
     /// Obtains [revm::Evm] instance and executes the given CREATE frame.
-    fn exec_create<DB: DatabaseExt>(
+    fn exec_create(
         &mut self,
         inputs: CreateInputs,
-        ccx: &mut CheatsCtxt<DB>,
-    ) -> Result<CreateOutcome, EVMError<DB::Error>> {
-        self.with_evm(ccx, |evm| {
+        ccx: &mut CheatsCtxt,
+    ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
+        with_evm(self, ccx, |evm| {
             evm.context.evm.inner.journaled_state.depth += 1;
 
             // Handle EOF bytecode
-            let first_frame_or_result = if evm.handler.cfg.spec_id.is_enabled_in(SpecId::PRAGUE_EOF)
-                && inputs.scheme == CreateScheme::Create && inputs.init_code.starts_with(&EOF_MAGIC_BYTES)
+            let first_frame_or_result = if evm.handler.cfg.spec_id.is_enabled_in(SpecId::PRAGUE_EOF) &&
+                inputs.scheme == CreateScheme::Create &&
+                inputs.init_code.starts_with(&EOF_MAGIC_BYTES)
             {
                 evm.handler.execution().eofcreate(
                     &mut evm.context,
-                    Box::new(EOFCreateInputs::new(inputs.caller, inputs.value, inputs.gas_limit, EOFCreateKind::Tx {
-                        initdata: inputs.init_code,
-                    })),
+                    Box::new(EOFCreateInputs::new(
+                        inputs.caller,
+                        inputs.value,
+                        inputs.gas_limit,
+                        EOFCreateKind::Tx { initdata: inputs.init_code },
+                    )),
                 )?
             } else {
                 evm.handler.execution().create(&mut evm.context, Box::new(inputs))?
@@ -158,8 +120,8 @@ pub trait CheatcodesExecutor {
         })
     }
 
-    fn console_log<DB: DatabaseExt>(&mut self, ccx: &mut CheatsCtxt<DB>, message: String) {
-        self.get_inspector::<DB>(ccx.state).console_log(message);
+    fn console_log(&mut self, ccx: &mut CheatsCtxt, message: String) {
+        self.get_inspector(ccx.state).console_log(message);
     }
 
     /// Returns a mutable reference to the tracing inspector if it is available.
@@ -168,17 +130,53 @@ pub trait CheatcodesExecutor {
     }
 }
 
+/// Constructs [revm::Evm] and runs a given closure with it.
+fn with_evm<E, F, O>(
+    executor: &mut E,
+    ccx: &mut CheatsCtxt,
+    f: F,
+) -> Result<O, EVMError<DatabaseError>>
+where
+    E: CheatcodesExecutor + ?Sized,
+    F: for<'a, 'b> FnOnce(
+        &mut revm::Evm<'_, &'b mut dyn InspectorExt, &'a mut dyn DatabaseExt>,
+    ) -> Result<O, EVMError<DatabaseError>>,
+{
+    let mut inspector = executor.get_inspector(ccx.state);
+    let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
+    let l1_block_info = std::mem::take(&mut ccx.ecx.l1_block_info);
+
+    let inner = revm::InnerEvmContext {
+        env: ccx.ecx.env.clone(),
+        journaled_state: std::mem::replace(
+            &mut ccx.ecx.journaled_state,
+            revm::JournaledState::new(Default::default(), Default::default()),
+        ),
+        db: &mut ccx.ecx.db as &mut dyn DatabaseExt,
+        error,
+        l1_block_info,
+    };
+
+    let mut evm = new_evm_with_existing_context(inner, &mut *inspector);
+
+    let res = f(&mut evm)?;
+
+    ccx.ecx.journaled_state = evm.context.evm.inner.journaled_state;
+    ccx.ecx.env = evm.context.evm.inner.env;
+    ccx.ecx.l1_block_info = evm.context.evm.inner.l1_block_info;
+    ccx.ecx.error = evm.context.evm.inner.error;
+
+    Ok(res)
+}
+
 /// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an
 /// inspector.
 #[derive(Debug, Default, Clone, Copy)]
 struct TransparentCheatcodesExecutor;
 
 impl CheatcodesExecutor for TransparentCheatcodesExecutor {
-    fn get_inspector<'a, DB: DatabaseExt>(
-        &'a mut self,
-        cheats: &'a mut Cheatcodes,
-    ) -> impl InspectorExt<DB> + 'a {
-        cheats
+    fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
+        Box::new(cheats)
     }
 }
 
@@ -240,15 +238,35 @@ pub struct GasMetering {
     pub touched: bool,
     /// True if gas metering should be reset to frame limit.
     pub reset: bool,
-    /// Stores frames paused gas.
+    /// Stores paused gas frames.
     pub paused_frames: Vec<Gas>,
 
+    /// The group and name of the active snapshot.
+    pub active_gas_snapshot: Option<(String, String)>,
+
     /// Cache of the amount of gas used in previous call.
     /// This is used by the `lastCallGas` cheatcode.
     pub last_call_gas: Option<crate::Vm::Gas>,
+
+    /// True if gas recording is enabled.
+    pub recording: bool,
+    /// The gas used in the last frame.
+    pub last_gas_used: u64,
+    /// Gas records for the active snapshots.
+    pub gas_records: Vec<GasRecord>,
 }
 
 impl GasMetering {
+    /// Start the gas recording.
+    pub fn start(&mut self) {
+        self.recording = true;
+    }
+
+    /// Stop the gas recording.
+    pub fn stop(&mut self) {
+        self.recording = false;
+    }
+
     /// Resume paused gas metering.
     pub fn resume(&mut self) {
         if self.paused {
@@ -294,13 +312,7 @@ impl ArbitraryStorage {
     /// Saves arbitrary storage value for a given address:
     /// - store value in changed values cache.
     /// - update account's storage with given value.
-    pub fn save<DB: DatabaseExt>(
-        &mut self,
-        ecx: &mut InnerEvmContext<DB>,
-        address: Address,
-        slot: U256,
-        data: U256,
-    ) {
+    pub fn save(&mut self, ecx: InnerEcx, address: Address, slot: U256, data: U256) {
         self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
         if let Ok(mut account) = ecx.load_account(address) {
             account.storage.insert(slot, EvmStorageSlot::new(data));
@@ -312,13 +324,7 @@ impl ArbitraryStorage {
     ///   existing value.
     /// - if no value was yet generated for given slot, then save new value in cache and update both
     ///   source and target storages.
-    pub fn copy<DB: DatabaseExt>(
-        &mut self,
-        ecx: &mut InnerEvmContext<DB>,
-        target: Address,
-        slot: U256,
-        new_value: U256,
-    ) -> U256 {
+    pub fn copy(&mut self, ecx: InnerEcx, target: Address, slot: U256, new_value: U256) -> U256 {
         let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
         let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
         let value = match storage_cache.get(&slot) {
@@ -446,6 +452,10 @@ pub struct Cheatcodes {
     /// Gas metering state.
     pub gas_metering: GasMetering,
 
+    /// Contains gas snapshots made over the course of a test suite.
+    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
+    pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
+
     /// Mapping slots.
     pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
 
@@ -506,6 +516,7 @@ impl Cheatcodes {
             serialized_jsons: Default::default(),
             eth_deals: Default::default(),
             gas_metering: Default::default(),
+            gas_snapshots: Default::default(),
             mapping_slots: Default::default(),
             pc: Default::default(),
             breakpoints: Default::default(),
@@ -522,11 +533,11 @@ impl Cheatcodes {
     }
 
     /// Decodes the input data and applies the cheatcode.
-    fn apply_cheatcode<DB: DatabaseExt, E: CheatcodesExecutor>(
+    fn apply_cheatcode(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: Ecx,
         call: &CallInputs,
-        executor: &mut E,
+        executor: &mut dyn CheatcodesExecutor,
     ) -> Result {
         // decode the cheatcode call
         let decoded = Vm::VmCalls::abi_decode(&call.input, false).map_err(|e| {
@@ -565,12 +576,7 @@ impl Cheatcodes {
     ///
     /// There may be cheatcodes in the constructor of the new contract, in order to allow them
     /// automatically we need to determine the new address.
-    fn allow_cheatcodes_on_create<DB: DatabaseExt>(
-        &self,
-        ecx: &mut InnerEvmContext<DB>,
-        caller: Address,
-        created_address: Address,
-    ) {
+    fn allow_cheatcodes_on_create(&self, ecx: InnerEcx, caller: Address, created_address: Address) {
         if ecx.journaled_state.depth <= 1 || ecx.db.has_cheatcode_access(&caller) {
             ecx.db.allow_cheatcode_access(created_address);
         }
@@ -580,7 +586,7 @@ impl Cheatcodes {
     ///
     /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's
     /// revert would run into issues.
-    pub fn on_revert<DB: DatabaseExt>(&mut self, ecx: &mut EvmContext<DB>) {
+    pub fn on_revert(&mut self, ecx: Ecx) {
         trace!(deals=?self.eth_deals.len(), "rolling back deals");
 
         // Delay revert clean up until expected revert is handled, if set.
@@ -604,14 +610,9 @@ impl Cheatcodes {
     }
 
     // common create functionality for both legacy and EOF.
-    fn create_common<DB, Input>(
-        &mut self,
-        ecx: &mut EvmContext<DB>,
-        mut input: Input,
-    ) -> Option<CreateOutcome>
+    fn create_common<Input>(&mut self, ecx: Ecx, mut input: Input) -> Option<CreateOutcome>
     where
-        DB: DatabaseExt,
-        Input: CommonCreateInput<DB>,
+        Input: CommonCreateInput,
     {
         let ecx = &mut ecx.inner;
         let gas = Gas::new(input.gas_limit());
@@ -664,11 +665,7 @@ impl Cheatcodes {
                             value: Some(input.value()),
                             input: TransactionInput::new(input.init_code()),
                             nonce: Some(account.info.nonce),
-                            gas: if is_fixed_gas_limit {
-                                Some(input.gas_limit() as u128)
-                            } else {
-                                None
-                            },
+                            gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
                             ..Default::default()
                         }
                         .into(),
@@ -708,14 +705,8 @@ impl Cheatcodes {
     }
 
     // common create_end functionality for both legacy and EOF.
-    fn create_end_common<DB>(
-        &mut self,
-        ecx: &mut EvmContext<DB>,
-        mut outcome: CreateOutcome,
-    ) -> CreateOutcome
-    where
-        DB: DatabaseExt,
-    {
+    fn create_end_common(&mut self, ecx: Ecx, mut outcome: CreateOutcome) -> CreateOutcome
+where {
         let ecx = &mut ecx.inner;
 
         // Clean up pranks
@@ -822,9 +813,9 @@ impl Cheatcodes {
         outcome
     }
 
-    pub fn call_with_executor<DB: DatabaseExt>(
+    pub fn call_with_executor(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: Ecx,
         call: &mut CallInputs,
         executor: &mut impl CheatcodesExecutor,
     ) -> Option<CallOutcome> {
@@ -1003,11 +994,7 @@ impl Cheatcodes {
                             value: call.transfer_value(),
                             input: TransactionInput::new(call.input.clone()),
                             nonce: Some(account.info.nonce),
-                            gas: if is_fixed_gas_limit {
-                                Some(call.gas_limit as u128)
-                            } else {
-                                None
-                            },
+                            gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
                             ..Default::default()
                         }
                         .into(),
@@ -1122,9 +1109,9 @@ impl Cheatcodes {
     }
 }
 
-impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
+impl Inspector<&mut dyn DatabaseExt> for Cheatcodes {
     #[inline]
-    fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
         // When the first interpreter is initialized we've circumvented the balance and gas checks,
         // so we apply our actual block data with the correct fees and all.
         if let Some(block) = self.block.take() {
@@ -1141,7 +1128,7 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
     }
 
     #[inline]
-    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
         self.pc = interpreter.program_counter();
 
         // `pauseGasMetering`: pause / resume interpreter gas.
@@ -1173,10 +1160,15 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
         if let Some(mapping_slots) = &mut self.mapping_slots {
             mapping::step(mapping_slots, interpreter);
         }
+
+        // `snapshotGas*`: take a snapshot of the current gas.
+        if self.gas_metering.recording {
+            self.meter_gas_record(interpreter, ecx);
+        }
     }
 
     #[inline]
-    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
         if self.gas_metering.paused {
             self.meter_gas_end(interpreter);
         }
@@ -1191,7 +1183,7 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
         }
     }
 
-    fn log(&mut self, interpreter: &mut Interpreter, _ecx: &mut EvmContext<DB>, log: &Log) {
+    fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: &Log) {
         if !self.expected_emits.is_empty() {
             expect::handle_expect_emit(self, log, interpreter);
         }
@@ -1206,16 +1198,11 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
         }
     }
 
-    fn call(&mut self, ecx: &mut EvmContext<DB>, inputs: &mut CallInputs) -> Option<CallOutcome> {
+    fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
         Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
     }
 
-    fn call_end(
-        &mut self,
-        ecx: &mut EvmContext<DB>,
-        call: &CallInputs,
-        mut outcome: CallOutcome,
-    ) -> CallOutcome {
+    fn call_end(&mut self, ecx: Ecx, call: &CallInputs, mut outcome: CallOutcome) -> CallOutcome {
         let ecx = &mut ecx.inner;
         let cheatcode_call = call.target_address == CHEATCODE_ADDRESS ||
             call.target_address == HARDHAT_CONSOLE_ADDRESS;
@@ -1511,34 +1498,26 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
         outcome
     }
 
-    fn create(
-        &mut self,
-        ecx: &mut EvmContext<DB>,
-        call: &mut CreateInputs,
-    ) -> Option<CreateOutcome> {
+    fn create(&mut self, ecx: Ecx, call: &mut CreateInputs) -> Option<CreateOutcome> {
         self.create_common(ecx, call)
     }
 
     fn create_end(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: Ecx,
         _call: &CreateInputs,
         outcome: CreateOutcome,
     ) -> CreateOutcome {
         self.create_end_common(ecx, outcome)
     }
 
-    fn eofcreate(
-        &mut self,
-        ecx: &mut EvmContext<DB>,
-        call: &mut EOFCreateInputs,
-    ) -> Option<CreateOutcome> {
+    fn eofcreate(&mut self, ecx: Ecx, call: &mut EOFCreateInputs) -> Option<CreateOutcome> {
         self.create_common(ecx, call)
     }
 
     fn eofcreate_end(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: Ecx,
         _call: &EOFCreateInputs,
         outcome: CreateOutcome,
     ) -> CreateOutcome {
@@ -1546,12 +1525,8 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
     }
 }
 
-impl<DB: DatabaseExt> InspectorExt<DB> for Cheatcodes {
-    fn should_use_create2_factory(
-        &mut self,
-        ecx: &mut EvmContext<DB>,
-        inputs: &mut CreateInputs,
-    ) -> bool {
+impl InspectorExt for Cheatcodes {
+    fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &mut CreateInputs) -> bool {
         if let CreateScheme::Create2 { .. } = inputs.scheme {
             let target_depth = if let Some(prank) = &self.prank {
                 prank.depth
@@ -1581,6 +1556,27 @@ impl Cheatcodes {
         }
     }
 
+    #[cold]
+    fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
+        if matches!(interpreter.instruction_result, InstructionResult::Continue) {
+            self.gas_metering.gas_records.iter_mut().for_each(|record| {
+                if ecx.journaled_state.depth() == record.depth {
+                    // Skip the first opcode of the first call frame as it includes the gas cost of
+                    // creating the snapshot.
+                    if self.gas_metering.last_gas_used != 0 {
+                        let gas_diff =
+                            interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
+                        record.gas_used = record.gas_used.saturating_add(gas_diff);
+                    }
+
+                    // Update `last_gas_used` to the current spent gas for the next iteration to
+                    // compare against.
+                    self.gas_metering.last_gas_used = interpreter.gas.spent();
+                }
+            });
+        }
+    }
+
     #[cold]
     fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
         // Remove recorded gas if we exit frame.
@@ -1617,11 +1613,7 @@ impl Cheatcodes {
     ///   cache) from mapped source address to the target address.
     /// - generates arbitrary value and saves it in target address storage.
     #[cold]
-    fn arbitrary_storage_end<DB: DatabaseExt>(
-        &mut self,
-        interpreter: &mut Interpreter,
-        ecx: &mut EvmContext<DB>,
-    ) {
+    fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
         let (key, target_address) = if interpreter.current_opcode() == op::SLOAD {
             (try_or_return!(interpreter.stack().peek(0)), interpreter.contract().target_address)
         } else {
@@ -1671,11 +1663,7 @@ impl Cheatcodes {
     }
 
     #[cold]
-    fn record_state_diffs<DB: DatabaseExt>(
-        &mut self,
-        interpreter: &mut Interpreter,
-        ecx: &mut EvmContext<DB>,
-    ) {
+    fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
         let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
         match interpreter.current_opcode() {
             op::SELFDESTRUCT => {
@@ -2022,10 +2010,7 @@ fn disallowed_mem_write(
 
 // Determines if the gas limit on a given call was manually set in the script and should therefore
 // not be overwritten by later estimations
-fn check_if_fixed_gas_limit<DB: DatabaseExt>(
-    ecx: &InnerEvmContext<DB>,
-    call_gas_limit: u64,
-) -> bool {
+fn check_if_fixed_gas_limit(ecx: InnerEcx, call_gas_limit: u64) -> bool {
     // If the gas limit was not set in the source code it is set to the estimated gas left at the
     // time of the call, which should be rather close to configured gas limit.
     // TODO: Find a way to reliably make this determination.
@@ -2095,44 +2080,25 @@ fn append_storage_access(
 }
 
 /// Dispatches the cheatcode call to the appropriate function.
-fn apply_dispatch<DB: DatabaseExt, E: CheatcodesExecutor>(
+fn apply_dispatch(
     calls: &Vm::VmCalls,
-    ccx: &mut CheatsCtxt<DB>,
-    executor: &mut E,
+    ccx: &mut CheatsCtxt,
+    executor: &mut dyn CheatcodesExecutor,
 ) -> Result {
-    // TODO: Replace with `<dyn Cheatcode>::apply_full` once it's object-safe.
-    macro_rules! dispatch {
-        ($($variant:ident),*) => {
-            match calls {
-                $(Vm::VmCalls::$variant(cheat) => crate::Cheatcode::apply_full(cheat, ccx, executor),)*
-            }
-        };
-    }
-
     let cheat = calls_as_dyn_cheatcode(calls);
+
+    let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
+    trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying");
+
     if let spec::Status::Deprecated(replacement) = *cheat.status() {
         ccx.state.deprecated.insert(cheat.signature(), replacement);
     }
-    let _guard = trace_span_and_call(cheat);
-    let mut result = vm_calls!(dispatch);
-    fill_and_trace_return(cheat, &mut result);
-    result
-}
-
-/// Helper function to check if frame execution will exit.
-fn will_exit(ir: InstructionResult) -> bool {
-    !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate)
-}
 
-fn trace_span_and_call(cheat: &dyn DynCheatcode) -> tracing::span::EnteredSpan {
-    let span = debug_span!(target: "cheatcodes", "apply", id = %cheat.id());
-    let entered = span.entered();
-    trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying");
-    entered
-}
+    // Apply the cheatcode.
+    let mut result = cheat.dyn_apply(ccx, executor);
 
-fn fill_and_trace_return(cheat: &dyn DynCheatcode, result: &mut Result) {
-    if let Err(e) = result {
+    // Format the error message to include the cheatcode name.
+    if let Err(e) = &mut result {
         if e.is_str() {
             let name = cheat.name();
             // Skip showing the cheatcode name for:
@@ -2143,13 +2109,16 @@ fn fill_and_trace_return(cheat: &dyn DynCheatcode, result: &mut Result) {
             }
         }
     }
+
     trace!(
         target: "cheatcodes",
-        return = %match result {
+        return = %match &result {
             Ok(b) => hex::encode(b),
             Err(e) => e.to_string(),
         }
     );
+
+    result
 }
 
 fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
@@ -2162,3 +2131,8 @@ fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
     }
     vm_calls!(as_dyn)
 }
+
+/// Helper function to check if frame execution will exit.
+fn will_exit(ir: InstructionResult) -> bool {
+    !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate)
+}
diff --git a/crates/cheatcodes/src/inspector/utils.rs b/crates/cheatcodes/src/inspector/utils.rs
index dfccd4b55552c..a0d7820aa3e27 100644
--- a/crates/cheatcodes/src/inspector/utils.rs
+++ b/crates/cheatcodes/src/inspector/utils.rs
@@ -1,13 +1,10 @@
+use super::InnerEcx;
 use crate::inspector::Cheatcodes;
 use alloy_primitives::{Address, Bytes, U256};
-use foundry_evm_core::backend::DatabaseExt;
-use revm::{
-    interpreter::{CreateInputs, CreateScheme, EOFCreateInputs, EOFCreateKind},
-    InnerEvmContext,
-};
+use revm::interpreter::{CreateInputs, CreateScheme, EOFCreateInputs, EOFCreateKind};
 
 /// Common behaviour of legacy and EOF create inputs.
-pub(crate) trait CommonCreateInput<DB: DatabaseExt> {
+pub(crate) trait CommonCreateInput {
     fn caller(&self) -> Address;
     fn gas_limit(&self) -> u64;
     fn value(&self) -> U256;
@@ -15,15 +12,11 @@ pub(crate) trait CommonCreateInput<DB: DatabaseExt> {
     fn scheme(&self) -> Option<CreateScheme>;
     fn set_caller(&mut self, caller: Address);
     fn log_debug(&self, cheatcode: &mut Cheatcodes, scheme: &CreateScheme);
-    fn allow_cheatcodes(
-        &self,
-        cheatcodes: &mut Cheatcodes,
-        ecx: &mut InnerEvmContext<DB>,
-    ) -> Address;
+    fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: InnerEcx) -> Address;
     fn computed_created_address(&self) -> Option<Address>;
 }
 
-impl<DB: DatabaseExt> CommonCreateInput<DB> for &mut CreateInputs {
+impl CommonCreateInput for &mut CreateInputs {
     fn caller(&self) -> Address {
         self.caller
     }
@@ -49,11 +42,7 @@ impl<DB: DatabaseExt> CommonCreateInput<DB> for &mut CreateInputs {
         };
         debug!(target: "cheatcodes", tx=?cheatcode.broadcastable_transactions.back().unwrap(), "broadcastable {kind}");
     }
-    fn allow_cheatcodes(
-        &self,
-        cheatcodes: &mut Cheatcodes,
-        ecx: &mut InnerEvmContext<DB>,
-    ) -> Address {
+    fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: InnerEcx) -> Address {
         let old_nonce = ecx
             .journaled_state
             .state
@@ -69,7 +58,7 @@ impl<DB: DatabaseExt> CommonCreateInput<DB> for &mut CreateInputs {
     }
 }
 
-impl<DB: DatabaseExt> CommonCreateInput<DB> for &mut EOFCreateInputs {
+impl CommonCreateInput for &mut EOFCreateInputs {
     fn caller(&self) -> Address {
         self.caller
     }
@@ -94,13 +83,9 @@ impl<DB: DatabaseExt> CommonCreateInput<DB> for &mut EOFCreateInputs {
     fn log_debug(&self, cheatcode: &mut Cheatcodes, _scheme: &CreateScheme) {
         debug!(target: "cheatcodes", tx=?cheatcode.broadcastable_transactions.back().unwrap(), "broadcastable eofcreate");
     }
-    fn allow_cheatcodes(
-        &self,
-        cheatcodes: &mut Cheatcodes,
-        ecx: &mut InnerEvmContext<DB>,
-    ) -> Address {
+    fn allow_cheatcodes(&self, cheatcodes: &mut Cheatcodes, ecx: InnerEcx) -> Address {
         let created_address =
-            <&mut EOFCreateInputs as CommonCreateInput<DB>>::computed_created_address(self)
+            <&mut EOFCreateInputs as CommonCreateInput>::computed_created_address(self)
                 .unwrap_or_default();
         cheatcodes.allow_cheatcodes_on_create(ecx, self.caller, created_address);
         created_address
diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs
index 50bd54701919d..a257289916cd7 100644
--- a/crates/cheatcodes/src/lib.rs
+++ b/crates/cheatcodes/src/lib.rs
@@ -70,7 +70,7 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode {
     ///
     /// Implement this function if you need access to the EVM data.
     #[inline(always)]
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         self.apply(ccx.state)
     }
 
@@ -78,58 +78,69 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode {
     ///
     /// Implement this function if you need access to the executor.
     #[inline(always)]
-    fn apply_full<DB: DatabaseExt, E: CheatcodesExecutor>(
-        &self,
-        ccx: &mut CheatsCtxt<DB>,
-        executor: &mut E,
-    ) -> Result {
+    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
         let _ = executor;
         self.apply_stateful(ccx)
     }
 }
 
 pub(crate) trait DynCheatcode {
-    fn name(&self) -> &'static str;
-    fn id(&self) -> &'static str;
-    fn signature(&self) -> &'static str;
-    fn status(&self) -> &Status<'static>;
-    fn as_debug(&self) -> &dyn std::fmt::Debug;
-}
+    fn cheatcode(&self) -> &'static spec::Cheatcode<'static>;
 
-impl<T: Cheatcode> DynCheatcode for T {
     fn name(&self) -> &'static str {
-        T::CHEATCODE.func.signature.split('(').next().unwrap()
+        self.cheatcode().func.signature.split('(').next().unwrap()
     }
+
     fn id(&self) -> &'static str {
-        T::CHEATCODE.func.id
+        self.cheatcode().func.id
     }
+
     fn signature(&self) -> &'static str {
-        T::CHEATCODE.func.signature
+        self.cheatcode().func.signature
     }
+
     fn status(&self) -> &Status<'static> {
-        &T::CHEATCODE.status
+        &self.cheatcode().status
     }
+
+    fn as_debug(&self) -> &dyn std::fmt::Debug;
+
+    fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result;
+}
+
+impl<T: Cheatcode> DynCheatcode for T {
+    #[inline]
+    fn cheatcode(&self) -> &'static spec::Cheatcode<'static> {
+        Self::CHEATCODE
+    }
+
+    #[inline]
     fn as_debug(&self) -> &dyn std::fmt::Debug {
         self
     }
+
+    #[inline]
+    fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
+        self.apply_full(ccx, executor)
+    }
 }
 
 /// The cheatcode context, used in `Cheatcode`.
-pub struct CheatsCtxt<'cheats, 'evm, DB: DatabaseExt> {
+pub struct CheatsCtxt<'cheats, 'evm, 'db, 'db2> {
     /// The cheatcodes inspector state.
     pub(crate) state: &'cheats mut Cheatcodes,
     /// The EVM data.
-    pub(crate) ecx: &'evm mut InnerEvmContext<DB>,
+    pub(crate) ecx: &'evm mut InnerEvmContext<&'db mut (dyn DatabaseExt + 'db2)>,
     /// The precompiles context.
-    pub(crate) precompiles: &'evm mut ContextPrecompiles<DB>,
+    pub(crate) precompiles: &'evm mut ContextPrecompiles<&'db mut (dyn DatabaseExt + 'db2)>,
     /// The original `msg.sender`.
     pub(crate) caller: Address,
     /// Gas limit of the current cheatcode call.
     pub(crate) gas_limit: u64,
 }
 
-impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, DB> {
-    type Target = InnerEvmContext<DB>;
+impl<'db, 'db2> std::ops::Deref for CheatsCtxt<'_, '_, 'db, 'db2> {
+    type Target = InnerEvmContext<&'db mut (dyn DatabaseExt + 'db2)>;
 
     #[inline(always)]
     fn deref(&self) -> &Self::Target {
@@ -137,14 +148,14 @@ impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'ev
     }
 }
 
-impl<'cheats, 'evm, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, 'evm, DB> {
+impl std::ops::DerefMut for CheatsCtxt<'_, '_, '_, '_> {
     #[inline(always)]
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut *self.ecx
     }
 }
 
-impl<'cheats, 'evm, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> {
+impl CheatsCtxt<'_, '_, '_, '_> {
     #[inline]
     pub(crate) fn is_precompile(&self, address: &Address) -> bool {
         self.precompiles.contains(address)
diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs
index 82eef2354994a..93d5aaaf8d349 100644
--- a/crates/cheatcodes/src/script.rs
+++ b/crates/cheatcodes/src/script.rs
@@ -1,6 +1,6 @@
 //! Implementations of [`Scripting`](spec::Group::Scripting) cheatcodes.
 
-use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*};
+use crate::{Cheatcode, CheatsCtxt, Result, Vm::*};
 use alloy_primitives::{Address, B256, U256};
 use alloy_signer_local::PrivateKeySigner;
 use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner};
@@ -8,49 +8,49 @@ use parking_lot::Mutex;
 use std::sync::Arc;
 
 impl Cheatcode for broadcast_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         broadcast(ccx, None, true)
     }
 }
 
 impl Cheatcode for broadcast_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { signer } = self;
         broadcast(ccx, Some(signer), true)
     }
 }
 
 impl Cheatcode for broadcast_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { privateKey } = self;
         broadcast_key(ccx, privateKey, true)
     }
 }
 
 impl Cheatcode for startBroadcast_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         broadcast(ccx, None, false)
     }
 }
 
 impl Cheatcode for startBroadcast_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { signer } = self;
         broadcast(ccx, Some(signer), false)
     }
 }
 
 impl Cheatcode for startBroadcast_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { privateKey } = self;
         broadcast_key(ccx, privateKey, false)
     }
 }
 
 impl Cheatcode for stopBroadcastCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         let Some(broadcast) = ccx.state.broadcast.take() else {
             bail!("no broadcast in progress to stop");
@@ -123,11 +123,7 @@ impl ScriptWallets {
 }
 
 /// Sets up broadcasting from a script using `new_origin` as the sender.
-fn broadcast<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
-    new_origin: Option<&Address>,
-    single_call: bool,
-) -> Result {
+fn broadcast(ccx: &mut CheatsCtxt, new_origin: Option<&Address>, single_call: bool) -> Result {
     ensure!(
         ccx.state.prank.is_none(),
         "you have an active prank; broadcasting and pranks are not compatible"
@@ -166,11 +162,7 @@ fn broadcast<DB: DatabaseExt>(
 /// Sets up broadcasting from a script with the sender derived from `private_key`.
 /// Adds this private key to `state`'s `script_wallets` vector to later be used for signing
 /// if broadcast is successful.
-fn broadcast_key<DB: DatabaseExt>(
-    ccx: &mut CheatsCtxt<DB>,
-    private_key: &U256,
-    single_call: bool,
-) -> Result {
+fn broadcast_key(ccx: &mut CheatsCtxt, private_key: &U256, single_call: bool) -> Result {
     let wallet = super::crypto::parse_wallet(private_key)?;
     let new_origin = wallet.address();
 
diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs
index cc91dba45b23d..bd723c9312ea2 100644
--- a/crates/cheatcodes/src/test.rs
+++ b/crates/cheatcodes/src/test.rs
@@ -1,26 +1,25 @@
 //! Implementations of [`Testing`](spec::Group::Testing) cheatcodes.
 
-use chrono::DateTime;
-use std::env;
-
-use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*};
+use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*};
 use alloy_primitives::Address;
 use alloy_sol_types::SolValue;
+use chrono::DateTime;
 use foundry_evm_core::constants::MAGIC_SKIP;
+use std::env;
 
 pub(crate) mod assert;
 pub(crate) mod assume;
 pub(crate) mod expect;
 
 impl Cheatcode for breakpoint_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { char } = self;
         breakpoint(ccx.state, &ccx.caller, char, true)
     }
 }
 
 impl Cheatcode for breakpoint_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { char, value } = self;
         breakpoint(ccx.state, &ccx.caller, char, *value)
     }
@@ -71,14 +70,14 @@ impl Cheatcode for sleepCall {
 }
 
 impl Cheatcode for skip_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { skipTest } = *self;
         skip_1Call { skipTest, reason: String::new() }.apply_stateful(ccx)
     }
 }
 
 impl Cheatcode for skip_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { skipTest, reason } = self;
         if *skipTest {
             // Skip should not work if called deeper than at test level.
diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs
index 4ab97c031e5a8..5161716fae56c 100644
--- a/crates/cheatcodes/src/test/assert.rs
+++ b/crates/cheatcodes/src/test/assert.rs
@@ -2,7 +2,7 @@ use crate::{CheatcodesExecutor, CheatsCtxt, Result, Vm::*};
 use alloy_primitives::{hex, I256, U256};
 use foundry_evm_core::{
     abi::{format_units_int, format_units_uint},
-    backend::{DatabaseExt, GLOBAL_FAIL_SLOT},
+    backend::GLOBAL_FAIL_SLOT,
     constants::CHEATCODE_ADDRESS,
 };
 use itertools::Itertools;
@@ -169,10 +169,10 @@ impl EqRelAssertionError<I256> {
 
 type ComparisonResult<'a, T> = Result<Vec<u8>, ComparisonAssertionError<'a, T>>;
 
-fn handle_assertion_result<DB: DatabaseExt, E: CheatcodesExecutor, ERR>(
+fn handle_assertion_result<ERR>(
     result: core::result::Result<Vec<u8>, ERR>,
-    ccx: &mut CheatsCtxt<DB>,
-    executor: &mut E,
+    ccx: &mut CheatsCtxt,
+    executor: &mut dyn CheatcodesExecutor,
     error_formatter: impl Fn(&ERR) -> String,
     error_msg: Option<&str>,
     format_error: bool,
@@ -224,10 +224,10 @@ macro_rules! impl_assertions {
     };
     (@impl $no_error:ident, $with_error:ident, ($($arg:ident),*), $body:expr, $error_formatter:expr, $format_error:literal) => {
         impl crate::Cheatcode for $no_error {
-            fn apply_full<DB: DatabaseExt, E: crate::CheatcodesExecutor>(
+            fn apply_full(
                 &self,
-                ccx: &mut CheatsCtxt<DB>,
-                executor: &mut E,
+                ccx: &mut CheatsCtxt,
+                executor: &mut dyn CheatcodesExecutor,
             ) -> Result {
                 let Self { $($arg),* } = self;
                 handle_assertion_result($body, ccx, executor, $error_formatter, None, $format_error)
@@ -235,10 +235,10 @@ macro_rules! impl_assertions {
         }
 
         impl crate::Cheatcode for $with_error {
-            fn apply_full<DB: DatabaseExt, E: crate::CheatcodesExecutor>(
+            fn apply_full(
                 &self,
-                ccx: &mut CheatsCtxt<DB>,
-                executor: &mut E,
+                ccx: &mut CheatsCtxt,
+                executor: &mut dyn CheatcodesExecutor,
             ) -> Result {
                 let Self { $($arg),*, error} = self;
                 handle_assertion_result($body, ccx, executor, $error_formatter, Some(error), $format_error)
diff --git a/crates/cheatcodes/src/test/assume.rs b/crates/cheatcodes/src/test/assume.rs
index e100eeb9d1c43..a0321b5a1cd38 100644
--- a/crates/cheatcodes/src/test/assume.rs
+++ b/crates/cheatcodes/src/test/assume.rs
@@ -1,5 +1,5 @@
 use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result};
-use foundry_evm_core::{backend::DatabaseExt, constants::MAGIC_ASSUME};
+use foundry_evm_core::constants::MAGIC_ASSUME;
 use spec::Vm::{assumeCall, assumeNoRevertCall};
 use std::fmt::Debug;
 
@@ -21,7 +21,7 @@ impl Cheatcode for assumeCall {
 }
 
 impl Cheatcode for assumeNoRevertCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         ccx.state.assume_no_revert =
             Some(AssumeNoRevert { depth: ccx.ecx.journaled_state.depth() });
         Ok(Default::default())
diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs
index f38776f94bfd5..7a58c7ab8aa81 100644
--- a/crates/cheatcodes/src/test/expect.rs
+++ b/crates/cheatcodes/src/test/expect.rs
@@ -1,4 +1,4 @@
-use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*};
+use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*};
 use alloy_primitives::{address, hex, Address, Bytes, LogData as RawLog, U256};
 use alloy_sol_types::{SolError, SolValue};
 use foundry_common::ContractsByArtifact;
@@ -210,7 +210,7 @@ impl Cheatcode for expectCallMinGas_1Call {
 }
 
 impl Cheatcode for expectEmit_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
         expect_emit(
             ccx.state,
@@ -223,7 +223,7 @@ impl Cheatcode for expectEmit_0Call {
 }
 
 impl Cheatcode for expectEmit_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
         expect_emit(
             ccx.state,
@@ -236,21 +236,21 @@ impl Cheatcode for expectEmit_1Call {
 }
 
 impl Cheatcode for expectEmit_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false)
     }
 }
 
 impl Cheatcode for expectEmit_3Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { emitter } = *self;
         expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false)
     }
 }
 
 impl Cheatcode for expectEmitAnonymous_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData } = *self;
         expect_emit(
             ccx.state,
@@ -263,7 +263,7 @@ impl Cheatcode for expectEmitAnonymous_0Call {
 }
 
 impl Cheatcode for expectEmitAnonymous_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self;
         expect_emit(
             ccx.state,
@@ -276,28 +276,28 @@ impl Cheatcode for expectEmitAnonymous_1Call {
 }
 
 impl Cheatcode for expectEmitAnonymous_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true)
     }
 }
 
 impl Cheatcode for expectEmitAnonymous_3Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { emitter } = *self;
         expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true)
     }
 }
 
 impl Cheatcode for expectRevert_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false, false, None)
     }
 }
 
 impl Cheatcode for expectRevert_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData } = self;
         expect_revert(
             ccx.state,
@@ -311,7 +311,7 @@ impl Cheatcode for expectRevert_1Call {
 }
 
 impl Cheatcode for expectRevert_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData } = self;
         expect_revert(
             ccx.state,
@@ -325,7 +325,7 @@ impl Cheatcode for expectRevert_2Call {
 }
 
 impl Cheatcode for expectRevert_3Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { reverter } = self;
         expect_revert(
             ccx.state,
@@ -339,7 +339,7 @@ impl Cheatcode for expectRevert_3Call {
 }
 
 impl Cheatcode for expectRevert_4Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData, reverter } = self;
         expect_revert(
             ccx.state,
@@ -353,7 +353,7 @@ impl Cheatcode for expectRevert_4Call {
 }
 
 impl Cheatcode for expectRevert_5Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData, reverter } = self;
         expect_revert(
             ccx.state,
@@ -367,7 +367,7 @@ impl Cheatcode for expectRevert_5Call {
 }
 
 impl Cheatcode for expectPartialRevert_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData } = self;
         expect_revert(
             ccx.state,
@@ -381,7 +381,7 @@ impl Cheatcode for expectPartialRevert_0Call {
 }
 
 impl Cheatcode for expectPartialRevert_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData, reverter } = self;
         expect_revert(
             ccx.state,
@@ -395,13 +395,13 @@ impl Cheatcode for expectPartialRevert_1Call {
 }
 
 impl Cheatcode for _expectCheatcodeRevert_0Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true, false, None)
     }
 }
 
 impl Cheatcode for _expectCheatcodeRevert_1Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData } = self;
         expect_revert(
             ccx.state,
@@ -415,7 +415,7 @@ impl Cheatcode for _expectCheatcodeRevert_1Call {
 }
 
 impl Cheatcode for _expectCheatcodeRevert_2Call {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { revertData } = self;
         expect_revert(
             ccx.state,
@@ -429,14 +429,14 @@ impl Cheatcode for _expectCheatcodeRevert_2Call {
 }
 
 impl Cheatcode for expectSafeMemoryCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { min, max } = *self;
         expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth())
     }
 }
 
 impl Cheatcode for stopExpectSafeMemoryCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self {} = self;
         ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth());
         Ok(Default::default())
@@ -444,7 +444,7 @@ impl Cheatcode for stopExpectSafeMemoryCall {
 }
 
 impl Cheatcode for expectSafeMemoryCallCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { min, max } = *self;
         expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth() + 1)
     }
diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs
index eb4d2f525a4fb..23cc02f7ad9de 100644
--- a/crates/cheatcodes/src/utils.rs
+++ b/crates/cheatcodes/src/utils.rs
@@ -1,12 +1,12 @@
 //! Implementations of [`Utilities`](spec::Group::Utilities) cheatcodes.
 
-use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*};
+use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*};
 use alloy_dyn_abi::{DynSolType, DynSolValue};
 use alloy_primitives::{map::HashMap, U256};
 use alloy_sol_types::SolValue;
 use foundry_common::ens::namehash;
-use foundry_evm_core::{backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER};
-use proptest::strategy::{Strategy, ValueTree};
+use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER;
+use proptest::prelude::Strategy;
 use rand::{Rng, RngCore};
 
 /// Contains locations of traces ignored via cheatcodes.
@@ -134,10 +134,10 @@ impl Cheatcode for randomBytesCall {
 }
 
 impl Cheatcode for pauseTracingCall {
-    fn apply_full<DB: DatabaseExt, E: crate::CheatcodesExecutor>(
+    fn apply_full(
         &self,
-        ccx: &mut crate::CheatsCtxt<DB>,
-        executor: &mut E,
+        ccx: &mut crate::CheatsCtxt,
+        executor: &mut dyn CheatcodesExecutor,
     ) -> Result {
         let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_ref()) else {
             // No tracer -> nothing to pause
@@ -157,10 +157,10 @@ impl Cheatcode for pauseTracingCall {
 }
 
 impl Cheatcode for resumeTracingCall {
-    fn apply_full<DB: DatabaseExt, E: crate::CheatcodesExecutor>(
+    fn apply_full(
         &self,
-        ccx: &mut crate::CheatsCtxt<DB>,
-        executor: &mut E,
+        ccx: &mut crate::CheatsCtxt,
+        executor: &mut dyn CheatcodesExecutor,
     ) -> Result {
         let Some(tracer) = executor.tracing_inspector().and_then(|t| t.as_ref()) else {
             // No tracer -> nothing to unpause
@@ -180,7 +180,7 @@ impl Cheatcode for resumeTracingCall {
 }
 
 impl Cheatcode for setArbitraryStorageCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { target } = self;
         ccx.state.arbitrary_storage().mark_arbitrary(target);
 
@@ -189,7 +189,7 @@ impl Cheatcode for setArbitraryStorageCall {
 }
 
 impl Cheatcode for copyStorageCall {
-    fn apply_stateful<DB: DatabaseExt>(&self, ccx: &mut CheatsCtxt<DB>) -> Result {
+    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
         let Self { from, to } = self;
 
         ensure!(
diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs
index 3d5fce2954547..5e40862f8277b 100644
--- a/crates/chisel/src/executor.rs
+++ b/crates/chisel/src/executor.rs
@@ -308,6 +308,7 @@ impl SessionSource {
                         self.config.evm_opts.clone(),
                         None,
                         None,
+                        None,
                         Some(self.solc.version.clone()),
                     )
                     .into(),
diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs
index 64c5207690d7a..a82853145799b 100644
--- a/crates/common/fmt/src/ui.rs
+++ b/crates/common/fmt/src/ui.rs
@@ -622,6 +622,7 @@ mixHash              {}
 nonce                {}
 number               {}
 parentHash           {}
+parentBeaconRoot     {}
 transactionsRoot     {}
 receiptsRoot         {}
 sha3Uncles           {}
@@ -642,6 +643,7 @@ totalDifficulty      {}",
         block.header.nonce.pretty(),
         block.header.number.pretty(),
         block.header.parent_hash.pretty(),
+        block.header.parent_beacon_block_root.pretty(),
         block.header.transactions_root.pretty(),
         block.header.receipts_root.pretty(),
         block.header.uncles_hash.pretty(),
diff --git a/crates/common/src/fs.rs b/crates/common/src/fs.rs
index 45c21eba69e2e..71a62d13a7ae7 100644
--- a/crates/common/src/fs.rs
+++ b/crates/common/src/fs.rs
@@ -56,6 +56,15 @@ pub fn write_json_file<T: Serialize>(path: &Path, obj: &T) -> Result<()> {
     writer.flush().map_err(|e| FsPathError::write(e, path))
 }
 
+/// Writes the object as a pretty JSON object.
+pub fn write_pretty_json_file<T: Serialize>(path: &Path, obj: &T) -> Result<()> {
+    let file = create_file(path)?;
+    let mut writer = BufWriter::new(file);
+    serde_json::to_writer_pretty(&mut writer, obj)
+        .map_err(|source| FsPathError::WriteJson { source, path: path.into() })?;
+    writer.flush().map_err(|e| FsPathError::write(e, path))
+}
+
 /// Wrapper for `std::fs::write`
 pub fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {
     let path = path.as_ref();
diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs
index 9df8f896f99d0..7335714e54806 100644
--- a/crates/common/src/transactions.rs
+++ b/crates/common/src/transactions.rs
@@ -2,7 +2,10 @@
 
 use alloy_consensus::{Transaction, TxEnvelope};
 use alloy_primitives::{Address, TxKind, U256};
-use alloy_provider::{network::AnyNetwork, Provider};
+use alloy_provider::{
+    network::{AnyNetwork, TransactionBuilder},
+    Provider,
+};
 use alloy_rpc_types::{AnyTransactionReceipt, BlockId, TransactionRequest};
 use alloy_serde::WithOtherFields;
 use alloy_transport::Transport;
@@ -212,8 +215,8 @@ impl TransactionMaybeSigned {
 
     pub fn gas(&self) -> Option<u128> {
         match self {
-            Self::Signed { tx, .. } => Some(tx.gas_limit()),
-            Self::Unsigned(tx) => tx.gas,
+            Self::Signed { tx, .. } => Some(tx.gas_limit() as u128),
+            Self::Unsigned(tx) => tx.gas_limit().map(|g| g as u128),
         }
     }
 
diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs
index 18352aab99729..017341c3d828f 100644
--- a/crates/config/src/lib.rs
+++ b/crates/config/src/lib.rs
@@ -175,6 +175,8 @@ pub struct Config {
     pub cache: bool,
     /// where the cache is stored if enabled
     pub cache_path: PathBuf,
+    /// where the gas snapshots are stored
+    pub snapshots: PathBuf,
     /// where the broadcast logs are stored
     pub broadcast: PathBuf,
     /// additional solc allow paths for `--allow-paths`
@@ -718,6 +720,7 @@ impl Config {
         self.out = p(&root, &self.out);
         self.broadcast = p(&root, &self.broadcast);
         self.cache_path = p(&root, &self.cache_path);
+        self.snapshots = p(&root, &self.snapshots);
 
         if let Some(build_info_path) = self.build_info_path {
             self.build_info_path = Some(p(&root, &build_info_path));
@@ -885,6 +888,12 @@ impl Config {
         remove_test_dir(&self.fuzz.failure_persist_dir);
         remove_test_dir(&self.invariant.failure_persist_dir);
 
+        // Remove snapshot directory.
+        let snapshot_dir = project.root().join(&self.snapshots);
+        if snapshot_dir.exists() {
+            let _ = fs::remove_dir_all(&snapshot_dir);
+        }
+
         Ok(())
     }
 
@@ -2086,6 +2095,7 @@ impl Default for Config {
             cache: true,
             cache_path: "cache".into(),
             broadcast: "broadcast".into(),
+            snapshots: "snapshots".into(),
             allow_paths: vec![],
             include_paths: vec![],
             force: false,
diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs
index 8156d75fbddfd..1589c8ee8ec9f 100644
--- a/crates/evm/core/src/backend/cow.rs
+++ b/crates/evm/core/src/backend/cow.rs
@@ -62,20 +62,16 @@ impl<'a> CowBackend<'a> {
     /// Note: in case there are any cheatcodes executed that modify the environment, this will
     /// update the given `env` with the new values.
     #[instrument(name = "inspect", level = "debug", skip_all)]
-    pub fn inspect<'b, I: InspectorExt<&'b mut dyn DatabaseExt>>(
-        &'b mut self,
+    pub fn inspect(
+        &mut self,
         env: &mut EnvWithHandlerCfg,
-        inspector: I,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<ResultAndState> {
         // this is a new call to inspect with a new env, so even if we've cloned the backend
         // already, we reset the initialized state
         self.is_initialized = false;
         self.spec_id = env.handler_cfg.spec_id;
-        let mut evm = crate::utils::new_evm_with_inspector(
-            self as &mut dyn DatabaseExt,
-            env.clone(),
-            inspector,
-        );
+        let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector);
 
         let res = evm.transact().wrap_err("backend: failed while inspecting")?;
 
@@ -190,7 +186,7 @@ impl<'a> DatabaseExt for CowBackend<'a> {
         transaction: B256,
         env: &mut Env,
         journaled_state: &mut JournaledState,
-        inspector: &mut dyn InspectorExt<Backend>,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<()> {
         self.backend_mut(env).transact(id, transaction, env, journaled_state, inspector)
     }
@@ -200,7 +196,7 @@ impl<'a> DatabaseExt for CowBackend<'a> {
         transaction: TransactionRequest,
         env: &Env,
         journaled_state: &mut JournaledState,
-        inspector: &mut dyn InspectorExt<Backend>,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<()> {
         self.backend_mut(env).transact_from_tx(transaction, env, journaled_state, inspector)
     }
diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs
index 49830bc892d03..9d26ce2873590 100644
--- a/crates/evm/core/src/backend/mod.rs
+++ b/crates/evm/core/src/backend/mod.rs
@@ -200,7 +200,7 @@ pub trait DatabaseExt: Database<Error = DatabaseError> + DatabaseCommit {
         transaction: B256,
         env: &mut Env,
         journaled_state: &mut JournaledState,
-        inspector: &mut dyn InspectorExt<Backend>,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<()>;
 
     /// Executes a given TransactionRequest, commits the new state to the DB
@@ -209,7 +209,7 @@ pub trait DatabaseExt: Database<Error = DatabaseError> + DatabaseCommit {
         transaction: TransactionRequest,
         env: &Env,
         journaled_state: &mut JournaledState,
-        inspector: &mut dyn InspectorExt<Backend>,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<()>;
 
     /// Returns the `ForkId` that's currently used in the database, if fork mode is on
@@ -751,17 +751,13 @@ impl Backend {
     /// Note: in case there are any cheatcodes executed that modify the environment, this will
     /// update the given `env` with the new values.
     #[instrument(name = "inspect", level = "debug", skip_all)]
-    pub fn inspect<'a, I: InspectorExt<&'a mut dyn DatabaseExt>>(
-        &'a mut self,
+    pub fn inspect(
+        &mut self,
         env: &mut EnvWithHandlerCfg,
-        inspector: I,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<ResultAndState> {
         self.initialize(env);
-        let mut evm = crate::utils::new_evm_with_inspector(
-            self as &mut dyn DatabaseExt,
-            env.clone(),
-            inspector,
-        );
+        let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector);
 
         let res = evm.transact().wrap_err("backend: failed while inspecting")?;
 
@@ -1229,7 +1225,7 @@ impl DatabaseExt for Backend {
         transaction: B256,
         env: &mut Env,
         journaled_state: &mut JournaledState,
-        inspector: &mut dyn InspectorExt<Self>,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<()> {
         trace!(?maybe_id, ?transaction, "execute transaction");
         let persistent_accounts = self.inner.persistent_accounts.clone();
@@ -1270,7 +1266,7 @@ impl DatabaseExt for Backend {
         tx: TransactionRequest,
         env: &Env,
         journaled_state: &mut JournaledState,
-        inspector: &mut dyn InspectorExt<Self>,
+        inspector: &mut dyn InspectorExt,
     ) -> eyre::Result<()> {
         trace!(?tx, "execute signed transaction");
 
@@ -1279,7 +1275,7 @@ impl DatabaseExt for Backend {
         env.tx.caller =
             tx.from.ok_or_else(|| eyre::eyre!("transact_from_tx: No `from` field found"))?;
         env.tx.gas_limit =
-            tx.gas.ok_or_else(|| eyre::eyre!("transact_from_tx: No `gas` field found"))? as u64;
+            tx.gas.ok_or_else(|| eyre::eyre!("transact_from_tx: No `gas` field found"))?;
         env.tx.gas_price = U256::from(tx.gas_price.or(tx.max_fee_per_gas).unwrap_or_default());
         env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from);
         env.tx.nonce = tx.nonce;
@@ -1294,9 +1290,9 @@ impl DatabaseExt for Backend {
         self.commit(journaled_state.state.clone());
 
         let res = {
-            let db = self.clone();
+            let mut db = self.clone();
             let env = self.env_with_handler_cfg(env);
-            let mut evm = new_evm_with_inspector(db, env, inspector);
+            let mut evm = new_evm_with_inspector(&mut db, env, inspector);
             evm.context.evm.journaled_state.depth = journaled_state.depth + 1;
             evm.transact()?
         };
@@ -1921,7 +1917,7 @@ fn commit_transaction(
     fork: &mut Fork,
     fork_id: &ForkId,
     persistent_accounts: &HashSet<Address>,
-    inspector: &mut dyn InspectorExt<Backend>,
+    inspector: &mut dyn InspectorExt,
 ) -> eyre::Result<()> {
     configure_tx_env(&mut env.env, tx);
 
@@ -1930,9 +1926,9 @@ fn commit_transaction(
         let fork = fork.clone();
         let journaled_state = journaled_state.clone();
         let depth = journaled_state.depth;
-        let db = Backend::new_with_fork(fork_id, fork, journaled_state);
+        let mut db = Backend::new_with_fork(fork_id, fork, journaled_state);
 
-        let mut evm = crate::utils::new_evm_with_inspector(db, env, inspector);
+        let mut evm = crate::utils::new_evm_with_inspector(&mut db as _, env, inspector);
         // Adjust inner EVM depth to ensure that inspectors receive accurate data.
         evm.context.evm.inner.journaled_state.depth = depth + 1;
         evm.transact().wrap_err("backend: failed committing transaction")?
diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs
index 0a8623a33cd0c..1a2ac4c4a0f49 100644
--- a/crates/evm/core/src/lib.rs
+++ b/crates/evm/core/src/lib.rs
@@ -6,7 +6,8 @@
 #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
 
 use auto_impl::auto_impl;
-use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, Database, EvmContext, Inspector};
+use backend::DatabaseExt;
+use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, EvmContext, Inspector};
 use revm_inspectors::access_list::AccessListInspector;
 
 #[macro_use]
@@ -33,14 +34,14 @@ pub mod utils;
 /// An extension trait that allows us to add additional hooks to Inspector for later use in
 /// handlers.
 #[auto_impl(&mut, Box)]
-pub trait InspectorExt<DB: Database>: Inspector<DB> {
+pub trait InspectorExt: for<'a> Inspector<&'a mut dyn DatabaseExt> {
     /// Determines whether the `DEFAULT_CREATE2_DEPLOYER` should be used for a CREATE2 frame.
     ///
     /// If this function returns true, we'll replace CREATE2 frame with a CALL frame to CREATE2
     /// factory.
     fn should_use_create2_factory(
         &mut self,
-        _context: &mut EvmContext<DB>,
+        _context: &mut EvmContext<&mut dyn DatabaseExt>,
         _inputs: &mut CreateInputs,
     ) -> bool {
         false
@@ -55,5 +56,6 @@ pub trait InspectorExt<DB: Database>: Inspector<DB> {
     }
 }
 
-impl<DB: Database> InspectorExt<DB> for NoOpInspector {}
-impl<DB: Database> InspectorExt<DB> for AccessListInspector {}
+impl InspectorExt for NoOpInspector {}
+
+impl InspectorExt for AccessListInspector {}
diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs
index 11f8bbb318827..d45c4b84908a8 100644
--- a/crates/evm/core/src/utils.rs
+++ b/crates/evm/core/src/utils.rs
@@ -1,5 +1,8 @@
 pub use crate::ic::*;
-use crate::{constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256, InspectorExt};
+use crate::{
+    backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER, precompiles::ALPHANET_P256,
+    InspectorExt,
+};
 use alloy_json_abi::{Function, JsonAbi};
 use alloy_primitives::{Address, Selector, TxKind, U256};
 use alloy_provider::{
@@ -8,8 +11,8 @@ use alloy_provider::{
 };
 use alloy_rpc_types::Transaction;
 use foundry_config::NamedChain;
+use foundry_fork_db::DatabaseError;
 use revm::{
-    db::WrapDatabaseRef,
     handler::register::EvmHandler,
     interpreter::{
         return_ok, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome,
@@ -84,7 +87,7 @@ pub fn get_function<'a>(
 /// Configures the env for the transaction
 pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) {
     env.tx.caller = tx.from;
-    env.tx.gas_limit = tx.gas as u64;
+    env.tx.gas_limit = tx.gas;
     env.tx.gas_price = U256::from(tx.gas_price.unwrap_or_default());
     env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from);
     env.tx.nonce = Some(tx.nonce);
@@ -123,15 +126,15 @@ fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInpu
 /// hook by inserting decoded address directly into interpreter.
 ///
 /// Should be installed after [revm::inspector_handle_register] and before any other registers.
-pub fn create2_handler_register<DB: revm::Database, I: InspectorExt<DB>>(
-    handler: &mut EvmHandler<'_, I, DB>,
+pub fn create2_handler_register<I: InspectorExt>(
+    handler: &mut EvmHandler<'_, I, &mut dyn DatabaseExt>,
 ) {
     let create2_overrides = Rc::<RefCell<Vec<_>>>::new(RefCell::new(Vec::new()));
 
     let create2_overrides_inner = create2_overrides.clone();
     let old_handle = handler.execution.create.clone();
     handler.execution.create =
-        Arc::new(move |ctx, mut inputs| -> Result<FrameOrResult, EVMError<DB::Error>> {
+        Arc::new(move |ctx, mut inputs| -> Result<FrameOrResult, EVMError<DatabaseError>> {
             let CreateScheme::Create2 { salt } = inputs.scheme else {
                 return old_handle(ctx, inputs);
             };
@@ -219,9 +222,7 @@ pub fn create2_handler_register<DB: revm::Database, I: InspectorExt<DB>>(
 }
 
 /// Adds Alphanet P256 precompile to the list of loaded precompiles.
-pub fn alphanet_handler_register<DB: revm::Database, I: InspectorExt<DB>>(
-    handler: &mut EvmHandler<'_, I, DB>,
-) {
+pub fn alphanet_handler_register<EXT, DB: revm::Database>(handler: &mut EvmHandler<'_, EXT, DB>) {
     let prev = handler.pre_execution.load_precompiles.clone();
     handler.pre_execution.load_precompiles = Arc::new(move || {
         let mut loaded_precompiles = prev();
@@ -233,15 +234,11 @@ pub fn alphanet_handler_register<DB: revm::Database, I: InspectorExt<DB>>(
 }
 
 /// Creates a new EVM with the given inspector.
-pub fn new_evm_with_inspector<'a, DB, I>(
-    db: DB,
+pub fn new_evm_with_inspector<'evm, 'i, 'db>(
+    db: &'db mut dyn DatabaseExt,
     env: revm::primitives::EnvWithHandlerCfg,
-    inspector: I,
-) -> revm::Evm<'a, I, DB>
-where
-    DB: revm::Database,
-    I: InspectorExt<DB>,
-{
+    inspector: &'i mut dyn InspectorExt,
+) -> revm::Evm<'evm, &'i mut dyn InspectorExt, &'db mut dyn DatabaseExt> {
     let revm::primitives::EnvWithHandlerCfg { env, handler_cfg } = env;
 
     // NOTE: We could use `revm::Evm::builder()` here, but on the current patch it has some
@@ -269,27 +266,10 @@ where
     revm::Evm::new(context, handler)
 }
 
-/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`.
-pub fn new_evm_with_inspector_ref<'a, DB, I>(
-    db: DB,
-    env: revm::primitives::EnvWithHandlerCfg,
-    inspector: I,
-) -> revm::Evm<'a, I, WrapDatabaseRef<DB>>
-where
-    DB: revm::DatabaseRef,
-    I: InspectorExt<WrapDatabaseRef<DB>>,
-{
-    new_evm_with_inspector(WrapDatabaseRef(db), env, inspector)
-}
-
-pub fn new_evm_with_existing_context<'a, DB, I>(
-    inner: revm::InnerEvmContext<DB>,
-    inspector: I,
-) -> revm::Evm<'a, I, DB>
-where
-    DB: revm::Database,
-    I: InspectorExt<DB>,
-{
+pub fn new_evm_with_existing_context<'a>(
+    inner: revm::InnerEvmContext<&'a mut dyn DatabaseExt>,
+    inspector: &'a mut dyn InspectorExt,
+) -> revm::Evm<'a, &'a mut dyn InspectorExt, &'a mut dyn DatabaseExt> {
     let handler_cfg = HandlerCfg::new(inner.spec_id());
 
     let mut handler = revm::Handler::new(handler_cfg);
@@ -303,24 +283,3 @@ where
         revm::Context::new(revm::EvmContext { inner, precompiles: Default::default() }, inspector);
     revm::Evm::new(context, handler)
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn build_evm() {
-        let mut db = revm::db::EmptyDB::default();
-
-        let env = Box::<revm::primitives::Env>::default();
-        let spec = SpecId::LATEST;
-        let handler_cfg = revm::primitives::HandlerCfg::new(spec);
-        let cfg = revm::primitives::EnvWithHandlerCfg::new(env, handler_cfg);
-
-        let mut inspector = revm::inspectors::NoOpInspector;
-
-        let mut evm = new_evm_with_inspector(&mut db, cfg, &mut inspector);
-        let result = evm.transact().unwrap();
-        assert!(result.result.is_success());
-    }
-}
diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs
index cb57f205326ab..982e44ea9d6a2 100644
--- a/crates/evm/evm/src/executors/fuzz/mod.rs
+++ b/crates/evm/evm/src/executors/fuzz/mod.rs
@@ -17,7 +17,7 @@ use foundry_evm_fuzz::{
 use foundry_evm_traces::SparsedTraceArena;
 use indicatif::ProgressBar;
 use proptest::test_runner::{TestCaseError, TestError, TestRunner};
-use std::cell::RefCell;
+use std::{cell::RefCell, collections::BTreeMap};
 
 mod types;
 pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome};
@@ -39,6 +39,8 @@ pub struct FuzzTestData {
     pub coverage: Option<HitMaps>,
     // Stores logs for all fuzz cases
     pub logs: Vec<Log>,
+    // Stores gas snapshots for all fuzz cases
+    pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
     // Deprecated cheatcodes mapped to their replacements.
     pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>,
 }
@@ -108,9 +110,11 @@ impl FuzzedExecutor {
                 FuzzOutcome::Case(case) => {
                     let mut data = execution_data.borrow_mut();
                     data.gas_by_case.push((case.case.gas, case.case.stipend));
+
                     if data.first_case.is_none() {
                         data.first_case.replace(case.case);
                     }
+
                     if let Some(call_traces) = case.traces {
                         if data.traces.len() == max_traces_to_collect {
                             data.traces.pop();
@@ -118,14 +122,17 @@ impl FuzzedExecutor {
                         data.traces.push(call_traces);
                         data.breakpoints.replace(case.breakpoints);
                     }
+
                     if show_logs {
                         data.logs.extend(case.logs);
                     }
+
                     // Collect and merge coverage if `forge snapshot` context.
                     match &mut data.coverage {
                         Some(prev) => prev.merge(case.coverage.unwrap()),
                         opt => *opt = case.coverage,
                     }
+
                     data.deprecated_cheatcodes = case.deprecated_cheatcodes;
 
                     Ok(())
diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs
index 03b677fcdf844..877101a8064f0 100644
--- a/crates/evm/evm/src/inspectors/logs.rs
+++ b/crates/evm/evm/src/inspectors/logs.rs
@@ -60,7 +60,7 @@ impl<DB: Database> Inspector<DB> for LogCollector {
                         gas: Gas::new(inputs.gas_limit),
                     },
                     memory_offset: inputs.return_memory_offset.clone(),
-                })
+                });
             }
         }
 
@@ -68,7 +68,7 @@ impl<DB: Database> Inspector<DB> for LogCollector {
     }
 }
 
-impl<DB: Database> InspectorExt<DB> for LogCollector {
+impl InspectorExt for LogCollector {
     fn console_log(&mut self, input: String) {
         self.logs.push(Log::new_unchecked(
             HARDHAT_CONSOLE_ADDRESS,
diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs
index fc270564133ad..c8df2c693102c 100644
--- a/crates/evm/evm/src/inspectors/stack.rs
+++ b/crates/evm/evm/src/inspectors/stack.rs
@@ -304,11 +304,8 @@ pub struct InspectorStackRefMut<'a> {
 }
 
 impl CheatcodesExecutor for InspectorStackInner {
-    fn get_inspector<'a, DB: DatabaseExt>(
-        &'a mut self,
-        cheats: &'a mut Cheatcodes,
-    ) -> impl InspectorExt<DB> + 'a {
-        InspectorStackRefMut { cheatcodes: Some(cheats), inner: self }
+    fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
+        Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
     }
 
     fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
@@ -479,15 +476,15 @@ impl<'a> InspectorStackRefMut<'a> {
     /// Should be called on the top-level call of inner context (depth == 0 &&
     /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility
     /// Updates tx.origin to the value before entering inner context
-    fn adjust_evm_data_for_inner_context<DB: DatabaseExt>(&mut self, ecx: &mut EvmContext<DB>) {
+    fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
         let inner_context_data =
             self.inner_context_data.as_ref().expect("should be called in inner context");
         ecx.env.tx.caller = inner_context_data.original_origin;
     }
 
-    fn do_call_end<DB: DatabaseExt>(
+    fn do_call_end(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         inputs: &CallInputs,
         outcome: CallOutcome,
     ) -> CallOutcome {
@@ -512,9 +509,9 @@ impl<'a> InspectorStackRefMut<'a> {
         outcome
     }
 
-    fn transact_inner<DB: DatabaseExt>(
+    fn transact_inner(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         transact_to: TransactTo,
         caller: Address,
         input: Bytes,
@@ -547,11 +544,7 @@ impl<'a> InspectorStackRefMut<'a> {
 
         let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id());
         let res = {
-            let mut evm = crate::utils::new_evm_with_inspector(
-                &mut ecx.db as &mut dyn DatabaseExt,
-                env,
-                &mut *self,
-            );
+            let mut evm = crate::utils::new_evm_with_inspector(&mut ecx.db, env, self);
             let res = evm.transact();
 
             // need to reset the env in case it was modified via cheatcodes during execution
@@ -630,8 +623,12 @@ impl<'a> InspectorStackRefMut<'a> {
     }
 }
 
-impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
-    fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+impl<'a> Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'a> {
+    fn initialize_interp(
+        &mut self,
+        interpreter: &mut Interpreter,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
+    ) {
         call_inspectors_adjust_depth!(
             [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer],
             |inspector| inspector.initialize_interp(interpreter, ecx),
@@ -640,7 +637,7 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
         );
     }
 
-    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
         call_inspectors_adjust_depth!(
             [
                 &mut self.fuzzer,
@@ -655,7 +652,11 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
         );
     }
 
-    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn step_end(
+        &mut self,
+        interpreter: &mut Interpreter,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
+    ) {
         call_inspectors_adjust_depth!(
             [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer],
             |inspector| inspector.step_end(interpreter, ecx),
@@ -664,7 +665,12 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
         );
     }
 
-    fn log(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>, log: &Log) {
+    fn log(
+        &mut self,
+        interpreter: &mut Interpreter,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
+        log: &Log,
+    ) {
         call_inspectors_adjust_depth!(
             [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
             |inspector| inspector.log(interpreter, ecx, log),
@@ -673,7 +679,11 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
         );
     }
 
-    fn call(&mut self, ecx: &mut EvmContext<DB>, call: &mut CallInputs) -> Option<CallOutcome> {
+    fn call(
+        &mut self,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
+        call: &mut CallInputs,
+    ) -> Option<CallOutcome> {
         if self.in_inner_context && ecx.journaled_state.depth == 0 {
             self.adjust_evm_data_for_inner_context(ecx);
             return None;
@@ -739,7 +749,7 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
 
     fn call_end(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         inputs: &CallInputs,
         outcome: CallOutcome,
     ) -> CallOutcome {
@@ -764,7 +774,7 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
 
     fn create(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         create: &mut CreateInputs,
     ) -> Option<CreateOutcome> {
         if self.in_inner_context && ecx.journaled_state.depth == 0 {
@@ -801,7 +811,7 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
 
     fn create_end(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         call: &CreateInputs,
         outcome: CreateOutcome,
     ) -> CreateOutcome {
@@ -835,7 +845,7 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
 
     fn eofcreate(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         create: &mut EOFCreateInputs,
     ) -> Option<CreateOutcome> {
         if self.in_inner_context && ecx.journaled_state.depth == 0 {
@@ -877,7 +887,7 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
 
     fn eofcreate_end(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         call: &EOFCreateInputs,
         outcome: CreateOutcome,
     ) -> CreateOutcome {
@@ -911,15 +921,15 @@ impl<'a, DB: DatabaseExt> Inspector<DB> for InspectorStackRefMut<'a> {
 
     fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
         call_inspectors!([&mut self.tracer, &mut self.printer], |inspector| {
-            Inspector::<DB>::selfdestruct(inspector, contract, target, value)
+            Inspector::<&mut dyn DatabaseExt>::selfdestruct(inspector, contract, target, value)
         });
     }
 }
 
-impl<'a, DB: DatabaseExt> InspectorExt<DB> for InspectorStackRefMut<'a> {
+impl InspectorExt for InspectorStackRefMut<'_> {
     fn should_use_create2_factory(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         inputs: &mut CreateInputs,
     ) -> bool {
         call_inspectors_adjust_depth!(
@@ -934,7 +944,7 @@ impl<'a, DB: DatabaseExt> InspectorExt<DB> for InspectorStackRefMut<'a> {
     }
 
     fn console_log(&mut self, input: String) {
-        call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::<DB>::console_log(
+        call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
             inspector, input
         ));
     }
@@ -944,20 +954,24 @@ impl<'a, DB: DatabaseExt> InspectorExt<DB> for InspectorStackRefMut<'a> {
     }
 }
 
-impl<DB: DatabaseExt> Inspector<DB> for InspectorStack {
+impl Inspector<&mut dyn DatabaseExt> for InspectorStack {
     #[inline]
-    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut dyn DatabaseExt>) {
         self.as_mut().step(interpreter, ecx)
     }
 
     #[inline]
-    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn step_end(
+        &mut self,
+        interpreter: &mut Interpreter,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
+    ) {
         self.as_mut().step_end(interpreter, ecx)
     }
 
     fn call(
         &mut self,
-        context: &mut EvmContext<DB>,
+        context: &mut EvmContext<&mut dyn DatabaseExt>,
         inputs: &mut CallInputs,
     ) -> Option<CallOutcome> {
         self.as_mut().call(context, inputs)
@@ -965,7 +979,7 @@ impl<DB: DatabaseExt> Inspector<DB> for InspectorStack {
 
     fn call_end(
         &mut self,
-        context: &mut EvmContext<DB>,
+        context: &mut EvmContext<&mut dyn DatabaseExt>,
         inputs: &CallInputs,
         outcome: CallOutcome,
     ) -> CallOutcome {
@@ -974,7 +988,7 @@ impl<DB: DatabaseExt> Inspector<DB> for InspectorStack {
 
     fn create(
         &mut self,
-        context: &mut EvmContext<DB>,
+        context: &mut EvmContext<&mut dyn DatabaseExt>,
         create: &mut CreateInputs,
     ) -> Option<CreateOutcome> {
         self.as_mut().create(context, create)
@@ -982,7 +996,7 @@ impl<DB: DatabaseExt> Inspector<DB> for InspectorStack {
 
     fn create_end(
         &mut self,
-        context: &mut EvmContext<DB>,
+        context: &mut EvmContext<&mut dyn DatabaseExt>,
         call: &CreateInputs,
         outcome: CreateOutcome,
     ) -> CreateOutcome {
@@ -991,7 +1005,7 @@ impl<DB: DatabaseExt> Inspector<DB> for InspectorStack {
 
     fn eofcreate(
         &mut self,
-        context: &mut EvmContext<DB>,
+        context: &mut EvmContext<&mut dyn DatabaseExt>,
         create: &mut EOFCreateInputs,
     ) -> Option<CreateOutcome> {
         self.as_mut().eofcreate(context, create)
@@ -999,30 +1013,39 @@ impl<DB: DatabaseExt> Inspector<DB> for InspectorStack {
 
     fn eofcreate_end(
         &mut self,
-        context: &mut EvmContext<DB>,
+        context: &mut EvmContext<&mut dyn DatabaseExt>,
         call: &EOFCreateInputs,
         outcome: CreateOutcome,
     ) -> CreateOutcome {
         self.as_mut().eofcreate_end(context, call, outcome)
     }
 
-    fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>) {
+    fn initialize_interp(
+        &mut self,
+        interpreter: &mut Interpreter,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
+    ) {
         self.as_mut().initialize_interp(interpreter, ecx)
     }
 
-    fn log(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<DB>, log: &Log) {
+    fn log(
+        &mut self,
+        interpreter: &mut Interpreter,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
+        log: &Log,
+    ) {
         self.as_mut().log(interpreter, ecx, log)
     }
 
     fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
-        Inspector::<DB>::selfdestruct(&mut self.as_mut(), contract, target, value)
+        Inspector::<&mut dyn DatabaseExt>::selfdestruct(&mut self.as_mut(), contract, target, value)
     }
 }
 
-impl<DB: DatabaseExt> InspectorExt<DB> for InspectorStack {
+impl InspectorExt for InspectorStack {
     fn should_use_create2_factory(
         &mut self,
-        ecx: &mut EvmContext<DB>,
+        ecx: &mut EvmContext<&mut dyn DatabaseExt>,
         inputs: &mut CreateInputs,
     ) -> bool {
         self.as_mut().should_use_create2_factory(ecx, inputs)
diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs
index 6bf1ffd8183a4..6d53a4756bd77 100644
--- a/crates/forge/bin/cmd/test/mod.rs
+++ b/crates/forge/bin/cmd/test/mod.rs
@@ -310,6 +310,17 @@ impl TestArgs {
         let toml = config.get_config_path();
         let profiles = get_available_profiles(toml)?;
 
+        // Remove the snapshots directory if it exists.
+        // This is to ensure that we don't have any stale snapshots.
+        // If `FORGE_SNAPSHOT_CHECK` is set, we don't remove the snapshots directory as it is
+        // required for comparison.
+        if std::env::var("FORGE_SNAPSHOT_CHECK").is_err() {
+            let snapshot_dir = project_root.join(&config.snapshots);
+            if snapshot_dir.exists() {
+                let _ = fs::remove_dir_all(project_root.join(&config.snapshots));
+            }
+        }
+
         let test_options: TestOptions = TestOptionsBuilder::default()
             .fuzz(config.fuzz.clone())
             .invariant(config.invariant.clone())
@@ -546,6 +557,8 @@ impl TestArgs {
             .gas_report
             .then(|| GasReport::new(config.gas_reports.clone(), config.gas_reports_ignore.clone()));
 
+        let mut gas_snapshots = BTreeMap::<String, BTreeMap<String, String>>::new();
+
         let mut outcome = TestOutcome::empty(self.allow_failure);
 
         let mut any_test_failed = false;
@@ -655,6 +668,83 @@ impl TestArgs {
                         }
                     }
                 }
+
+                // Collect and merge gas snapshots.
+                for (group, new_snapshots) in result.gas_snapshots.iter() {
+                    gas_snapshots.entry(group.clone()).or_default().extend(new_snapshots.clone());
+                }
+            }
+
+            // Write gas snapshots to disk if any were collected.
+            if !gas_snapshots.is_empty() {
+                // Check for differences in gas snapshots if `FORGE_SNAPSHOT_CHECK` is set.
+                // Exiting early with code 1 if differences are found.
+                if std::env::var("FORGE_SNAPSHOT_CHECK").is_ok() {
+                    let differences_found = gas_snapshots.clone().into_iter().fold(
+                        false,
+                        |mut found, (group, snapshots)| {
+                            // If the snapshot file doesn't exist, we can't compare so we skip.
+                            if !&config.snapshots.join(format!("{group}.json")).exists() {
+                                return false;
+                            }
+
+                            let previous_snapshots: BTreeMap<String, String> =
+                                fs::read_json_file(&config.snapshots.join(format!("{group}.json")))
+                                    .expect("Failed to read snapshots from disk");
+
+                            let diff: BTreeMap<_, _> = snapshots
+                                .iter()
+                                .filter_map(|(k, v)| {
+                                    previous_snapshots.get(k).and_then(|previous_snapshot| {
+                                        if previous_snapshot != v {
+                                            Some((
+                                                k.clone(),
+                                                (previous_snapshot.clone(), v.clone()),
+                                            ))
+                                        } else {
+                                            None
+                                        }
+                                    })
+                                })
+                                .collect();
+
+                            if !diff.is_empty() {
+                                println!(
+                                    "{}",
+                                    format!("\n[{group}] Failed to match snapshots:").red().bold()
+                                );
+
+                                for (key, (previous_snapshot, snapshot)) in &diff {
+                                    println!(
+                                        "{}",
+                                        format!("- [{key}] {previous_snapshot} → {snapshot}").red()
+                                    );
+                                }
+
+                                found = true;
+                            }
+
+                            found
+                        },
+                    );
+
+                    if differences_found {
+                        println!();
+                        eyre::bail!("Snapshots differ from previous run");
+                    }
+                }
+
+                // Create `snapshots` directory if it doesn't exist.
+                fs::create_dir_all(&config.snapshots)?;
+
+                // Write gas snapshots to disk per group.
+                gas_snapshots.clone().into_iter().for_each(|(group, snapshots)| {
+                    fs::write_pretty_json_file(
+                        &config.snapshots.join(format!("{group}.json")),
+                        &snapshots,
+                    )
+                    .expect("Failed to write gas snapshots to disk");
+                });
             }
 
             // Print suite summary.
diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs
index 43aade0ff65c1..802ad3884cc65 100644
--- a/crates/forge/src/multi_runner.rs
+++ b/crates/forge/src/multi_runner.rs
@@ -243,6 +243,7 @@ impl MultiContractRunner {
             self.evm_opts.clone(),
             Some(self.known_contracts.clone()),
             None,
+            Some(artifact_id.name.clone()),
             Some(artifact_id.version.clone()),
         );
 
diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs
index 1ec829f6bc942..0e00bb5e2773e 100644
--- a/crates/forge/src/result.rs
+++ b/crates/forge/src/result.rs
@@ -412,6 +412,9 @@ pub struct TestResult {
     /// pc breakpoint char map
     pub breakpoints: Breakpoints,
 
+    /// Any captured gas snapshots along the test's execution which should be accumulated.
+    pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
+
     /// Deprecated cheatcodes (mapped to their replacements, if any) used in current test.
     #[serde(skip)]
     pub deprecated_cheatcodes: HashMap<&'static str, Option<&'static str>>,
@@ -531,6 +534,7 @@ impl TestResult {
 
         if let Some(cheatcodes) = raw_call_result.cheatcodes {
             self.breakpoints = cheatcodes.breakpoints;
+            self.gas_snapshots = cheatcodes.gas_snapshots;
             self.deprecated_cheatcodes = cheatcodes.deprecated;
         }
 
diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs
index f8b1da523c74c..5c0d62a3299af 100644
--- a/crates/forge/tests/cli/config.rs
+++ b/crates/forge/tests/cli/config.rs
@@ -37,6 +37,7 @@ forgetest!(can_extract_config_values, |prj, cmd| {
         libs: vec!["lib-test".into()],
         cache: true,
         cache_path: "test-cache".into(),
+        snapshots: "snapshots".into(),
         broadcast: "broadcast".into(),
         force: true,
         evm_version: EvmVersion::Byzantium,
diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs
index ac893b19f7aab..3faaa441e24c2 100644
--- a/crates/script/src/broadcast.rs
+++ b/crates/script/src/broadcast.rs
@@ -43,7 +43,7 @@ where
 
     tx.set_gas_limit(
         provider.estimate_gas(tx).await.wrap_err("Failed to estimate gas for tx")? *
-            estimate_multiplier as u128 /
+            estimate_multiplier /
             100,
     );
     Ok(())
diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs
index 8231a5d54b561..94c028bb9b471 100644
--- a/crates/script/src/lib.rs
+++ b/crates/script/src/lib.rs
@@ -612,6 +612,7 @@ impl ScriptConfig {
                             self.evm_opts.clone(),
                             Some(known_contracts),
                             Some(script_wallets),
+                            Some(target.name),
                             Some(target.version),
                         )
                         .into(),
diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs
index bbf84ff0ff8ae..639c5f022ac18 100644
--- a/crates/script/src/transaction.rs
+++ b/crates/script/src/transaction.rs
@@ -121,7 +121,7 @@ impl TransactionWithMetadata {
         if !self.is_fixed_gas_limit {
             if let Some(unsigned) = self.transaction.as_unsigned_mut() {
                 // We inflate the gas used by the user specified percentage
-                unsigned.gas = Some((result.gas_used * gas_estimate_multiplier / 100) as u128);
+                unsigned.gas = Some(result.gas_used * gas_estimate_multiplier / 100);
             }
         }
 
diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs
index 034bb49a68461..661eb5c8dcb33 100644
--- a/crates/verify/src/bytecode.rs
+++ b/crates/verify/src/bytecode.rs
@@ -253,9 +253,9 @@ impl VerifyBytecodeArgs {
 
             if let Some(ref block) = genesis_block {
                 configure_env_block(&mut env, block);
-                gen_tx.max_fee_per_gas = Some(block.header.base_fee_per_gas.unwrap_or_default());
+                gen_tx.max_fee_per_gas = block.header.base_fee_per_gas.map(|g| g as u128);
                 gen_tx.gas = block.header.gas_limit;
-                gen_tx.gas_price = Some(block.header.base_fee_per_gas.unwrap_or_default());
+                gen_tx.gas_price = block.header.base_fee_per_gas.map(|g| g as u128);
             }
 
             configure_tx_env(&mut env, &gen_tx);
diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs
index 28c3e31a168d5..c593e67e3d510 100644
--- a/crates/wallets/src/multi_wallet.rs
+++ b/crates/wallets/src/multi_wallet.rs
@@ -162,7 +162,7 @@ pub struct MultiWalletOpts {
     )]
     pub mnemonic_indexes: Option<Vec<u32>>,
 
-    /// Use the keystore in the given folder or file.
+    /// Use the keystore by its filename in the given folder.
     #[arg(
         long = "keystore",
         visible_alias = "keystores",
@@ -173,7 +173,7 @@ pub struct MultiWalletOpts {
     #[builder(default = "None")]
     pub keystore_paths: Option<Vec<String>>,
 
-    /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename
+    /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename.
     #[arg(
         long = "account",
         visible_alias = "accounts",
diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol
index fc0b66c325c41..6bb6ad46c0a40 100644
--- a/testdata/cheats/Vm.sol
+++ b/testdata/cheats/Vm.sol
@@ -424,7 +424,11 @@ interface Vm {
     function skip(bool skipTest, string calldata reason) external;
     function sleep(uint256 duration) external;
     function snapshot() external returns (uint256 snapshotId);
+    function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed);
+    function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed);
     function snapshotState() external returns (uint256 snapshotId);
+    function snapshotValue(string calldata name, uint256 value) external;
+    function snapshotValue(string calldata group, string calldata name, uint256 value) external;
     function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs);
     function startBroadcast() external;
     function startBroadcast(address signer) external;
@@ -433,6 +437,8 @@ interface Vm {
     function startMappingRecording() external;
     function startPrank(address msgSender) external;
     function startPrank(address msgSender, address txOrigin) external;
+    function startSnapshotGas(string calldata name) external;
+    function startSnapshotGas(string calldata group, string calldata name) external;
     function startStateDiffRecording() external;
     function stopAndReturnDebugTraceRecording() external returns (DebugStep[] memory step);
     function stopAndReturnStateDiff() external returns (AccountAccess[] memory accountAccesses);
@@ -440,6 +446,9 @@ interface Vm {
     function stopExpectSafeMemory() external;
     function stopMappingRecording() external;
     function stopPrank() external;
+    function stopSnapshotGas() external returns (uint256 gasUsed);
+    function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed);
+    function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed);
     function store(address target, bytes32 slot, bytes32 value) external;
     function toBase64URL(bytes calldata data) external pure returns (string memory);
     function toBase64URL(string calldata data) external pure returns (string memory);
diff --git a/testdata/default/cheats/GasSnapshots.t.sol b/testdata/default/cheats/GasSnapshots.t.sol
new file mode 100644
index 0000000000000..1e64a073d11fd
--- /dev/null
+++ b/testdata/default/cheats/GasSnapshots.t.sol
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+pragma solidity 0.8.18;
+
+import "ds-test/test.sol";
+import "cheats/Vm.sol";
+
+contract GasSnapshotTest is DSTest {
+    Vm constant vm = Vm(HEVM_ADDRESS);
+
+    uint256 public slot0;
+    Flare public flare;
+
+    function setUp() public {
+        flare = new Flare();
+    }
+
+    function testSnapshotGasSectionExternal() public {
+        vm.startSnapshotGas("testAssertGasExternal");
+        flare.run(1);
+        uint256 gasUsed = vm.stopSnapshotGas();
+
+        assertGt(gasUsed, 0);
+    }
+
+    function testSnapshotGasSectionInternal() public {
+        vm.startSnapshotGas("testAssertGasInternalA");
+        slot0 = 1;
+        vm.stopSnapshotGas();
+
+        vm.startSnapshotGas("testAssertGasInternalB");
+        slot0 = 2;
+        vm.stopSnapshotGas();
+
+        vm.startSnapshotGas("testAssertGasInternalC");
+        slot0 = 0;
+        vm.stopSnapshotGas();
+
+        vm.startSnapshotGas("testAssertGasInternalD");
+        slot0 = 1;
+        vm.stopSnapshotGas();
+
+        vm.startSnapshotGas("testAssertGasInternalE");
+        slot0 = 2;
+        vm.stopSnapshotGas();
+    }
+
+    // Writes to `GasSnapshotTest` group with custom names.
+    function testSnapshotValueDefaultGroupA() public {
+        uint256 a = 123;
+        uint256 b = 456;
+        uint256 c = 789;
+
+        vm.snapshotValue("a", a);
+        vm.snapshotValue("b", b);
+        vm.snapshotValue("c", c);
+    }
+
+    // Writes to same `GasSnapshotTest` group with custom names.
+    function testSnapshotValueDefaultGroupB() public {
+        uint256 d = 123;
+        uint256 e = 456;
+        uint256 f = 789;
+
+        vm.snapshotValue("d", d);
+        vm.snapshotValue("e", e);
+        vm.snapshotValue("f", f);
+    }
+
+    // Writes to `CustomGroup` group with custom names.
+    // Asserts that the order of the values is alphabetical.
+    function testSnapshotValueCustomGroupA() public {
+        uint256 o = 123;
+        uint256 i = 456;
+        uint256 q = 789;
+
+        vm.snapshotValue("CustomGroup", "q", q);
+        vm.snapshotValue("CustomGroup", "i", i);
+        vm.snapshotValue("CustomGroup", "o", o);
+    }
+
+    // Writes to `CustomGroup` group with custom names.
+    // Asserts that the order of the values is alphabetical.
+    function testSnapshotValueCustomGroupB() public {
+        uint256 x = 123;
+        uint256 e = 456;
+        uint256 z = 789;
+
+        vm.snapshotValue("CustomGroup", "z", z);
+        vm.snapshotValue("CustomGroup", "x", x);
+        vm.snapshotValue("CustomGroup", "e", e);
+    }
+
+    // Writes to `GasSnapshotTest` group with `testSnapshotGasDefault` name.
+    function testSnapshotGasSectionDefaultGroupStop() public {
+        vm.startSnapshotGas("testSnapshotGasSection");
+
+        flare.run(256);
+
+        // vm.stopSnapshotGas() will use the last snapshot name.
+        uint256 gasUsed = vm.stopSnapshotGas();
+        assertGt(gasUsed, 0);
+    }
+
+    // Writes to `GasSnapshotTest` group with `testSnapshotGasCustom` name.
+    function testSnapshotGasSectionCustomGroupStop() public {
+        vm.startSnapshotGas("CustomGroup", "testSnapshotGasSection");
+
+        flare.run(256);
+
+        // vm.stopSnapshotGas() will use the last snapshot name, even with custom group.
+        uint256 gasUsed = vm.stopSnapshotGas();
+        assertGt(gasUsed, 0);
+    }
+
+    // Writes to `GasSnapshotTest` group with `testSnapshotGasSection` name.
+    function testSnapshotGasSectionName() public {
+        vm.startSnapshotGas("testSnapshotGasSectionName");
+
+        flare.run(256);
+
+        uint256 gasUsed = vm.stopSnapshotGas("testSnapshotGasSectionName");
+        assertGt(gasUsed, 0);
+    }
+
+    // Writes to `CustomGroup` group with `testSnapshotGasSection` name.
+    function testSnapshotGasSectionGroupName() public {
+        vm.startSnapshotGas("CustomGroup", "testSnapshotGasSectionGroupName");
+
+        flare.run(256);
+
+        uint256 gasUsed = vm.stopSnapshotGas("CustomGroup", "testSnapshotGasSectionGroupName");
+        assertGt(gasUsed, 0);
+    }
+
+    // Writes to `GasSnapshotTest` group with `testSnapshotGas` name.
+    function testSnapshotGasLastCallName() public {
+        flare.run(1);
+
+        uint256 gasUsed = vm.snapshotGasLastCall("testSnapshotGasLastCallName");
+        assertGt(gasUsed, 0);
+    }
+
+    // Writes to `CustomGroup` group with `testSnapshotGas` name.
+    function testSnapshotGasLastCallGroupName() public {
+        flare.run(1);
+
+        uint256 gasUsed = vm.snapshotGasLastCall("CustomGroup", "testSnapshotGasLastCallGroupName");
+        assertGt(gasUsed, 0);
+    }
+}
+
+contract GasComparisonTest is DSTest {
+    Vm constant vm = Vm(HEVM_ADDRESS);
+
+    uint256 public slot0;
+    uint256 public slot1;
+
+    uint256 public cachedGas;
+
+    function testGasComparisonEmpty() public {
+        // Start a cheatcode snapshot.
+        vm.startSnapshotGas("ComparisonGroup", "testGasComparisonEmptyA");
+        uint256 a = vm.stopSnapshotGas();
+
+        // Start a comparitive Solidity snapshot.
+        _snapStart();
+        uint256 b = _snapEnd();
+        vm.snapshotValue("ComparisonGroup", "testGasComparisonEmptyB", b);
+
+        assertEq(a, b);
+    }
+
+    function testGasComparisonInternalCold() public {
+        // Start a cheatcode snapshot.
+        vm.startSnapshotGas("ComparisonGroup", "testGasComparisonInternalColdA");
+        slot0 = 1;
+        uint256 a = vm.stopSnapshotGas();
+
+        // Start a comparitive Solidity snapshot.
+        _snapStart();
+        slot1 = 1;
+        uint256 b = _snapEnd();
+        vm.snapshotValue("ComparisonGroup", "testGasComparisonInternalColdB", b);
+
+        vm.assertApproxEqAbs(a, b, 6);
+    }
+
+    function testGasComparisonInternalWarm() public {
+        // Warm up the cache.
+        slot0 = 1;
+
+        // Start a cheatcode snapshot.
+        vm.startSnapshotGas("ComparisonGroup", "testGasComparisonInternalWarmA");
+        slot0 = 2;
+        uint256 a = vm.stopSnapshotGas();
+
+        // Start a comparitive Solidity snapshot.
+        _snapStart();
+        slot0 = 3;
+        uint256 b = _snapEnd();
+        vm.snapshotValue("ComparisonGroup", "testGasComparisonInternalWarmB", b);
+
+        vm.assertApproxEqAbs(a, b, 6);
+    }
+
+    function testGasComparisonExternal() public {
+        // Warm up the cache.
+        TargetB target = new TargetB();
+        target.update(1);
+
+        // Start a cheatcode snapshot.
+        vm.startSnapshotGas("ComparisonGroup", "testGasComparisonExternalA");
+        target.update(2);
+        uint256 a = vm.stopSnapshotGas();
+
+        // Start a comparitive Solidity snapshot.
+        _snapStart();
+        target.update(3);
+        uint256 b = _snapEnd();
+        vm.snapshotValue("ComparisonGroup", "testGasComparisonExternalB", b);
+
+        assertEq(a, b);
+    }
+
+    function testGasComparisonCreate() public {
+        // Start a cheatcode snapshot.
+        vm.startSnapshotGas("ComparisonGroup", "testGasComparisonCreateA");
+        new TargetC();
+        uint256 a = vm.stopSnapshotGas();
+
+        // Start a comparitive Solidity snapshot.
+        _snapStart();
+        new TargetC();
+        uint256 b = _snapEnd();
+        vm.snapshotValue("ComparisonGroup", "testGasComparisonCreateB", b);
+
+        assertEq(a, b);
+    }
+
+    function testGasComparisonNestedCalls() public {
+        // Warm up the cache.
+        TargetA target = new TargetA();
+        target.update(1);
+
+        // Start a cheatcode snapshot.
+        vm.startSnapshotGas("ComparisonGroup", "testGasComparisonNestedCallsA");
+        target.update(2);
+        uint256 a = vm.stopSnapshotGas();
+
+        // Start a comparitive Solidity snapshot.
+        _snapStart();
+        target.update(3);
+        uint256 b = _snapEnd();
+        vm.snapshotValue("ComparisonGroup", "testGasComparisonNestedCallsB", b);
+
+        assertEq(a, b);
+    }
+
+    function testGasComparisonFlare() public {
+        // Warm up the cache.
+        Flare flare = new Flare();
+        flare.run(1);
+
+        // Start a cheatcode snapshot.
+        vm.startSnapshotGas("ComparisonGroup", "testGasComparisonFlareA");
+        flare.run(256);
+        uint256 a = vm.stopSnapshotGas();
+
+        // Start a comparitive Solidity snapshot.
+        _snapStart();
+        flare.run(256);
+        uint256 b = _snapEnd();
+        vm.snapshotValue("ComparisonGroup", "testGasComparisonFlareB", b);
+
+        assertEq(a, b);
+    }
+
+    // Internal function to start a Solidity snapshot.
+    function _snapStart() internal {
+        cachedGas = 1;
+        cachedGas = gasleft();
+    }
+
+    // Internal function to end a Solidity snapshot.
+    function _snapEnd() internal returns (uint256 gasUsed) {
+        gasUsed = cachedGas - gasleft() - 138;
+        cachedGas = 2;
+    }
+}
+
+contract Flare {
+    bytes32[] public data;
+
+    function run(uint256 n_) public {
+        for (uint256 i = 0; i < n_; i++) {
+            data.push(keccak256(abi.encodePacked(i)));
+        }
+    }
+}
+
+contract TargetA {
+    TargetB public target;
+
+    constructor() {
+        target = new TargetB();
+    }
+
+    function update(uint256 x_) public {
+        target.update(x_);
+    }
+}
+
+contract TargetB {
+    uint256 public x;
+
+    function update(uint256 x_) public {
+        x = x_;
+    }
+}
+
+contract TargetC {}