Skip to main content

evm_fork_cache/
deploy.rs

1//! Helpers for deploying Foundry artifacts into an [`EvmCache`].
2//!
3//! The main use case is local simulation against a fork where the caller wants
4//! to replace code at an existing address while preserving that account's
5//! storage, balance, and nonce. For contracts with immutables, the helper first
6//! runs the creation bytecode with ABI-encoded constructor arguments, then
7//! copies the resulting runtime bytecode to the target address.
8
9use std::path::{Path, PathBuf};
10
11use alloy_primitives::{Address, Bytes};
12use alloy_sol_types::{SolType, SolValue, abi::TokenSeq};
13use anyhow::{Context, Result, bail};
14use tracing::{debug, info};
15
16use crate::cache::{EvmCache, MissingTargetBehavior};
17
18/// A Foundry JSON artifact with decoded creation bytecode.
19#[derive(Debug, Clone)]
20pub struct FoundryArtifact {
21    /// Path the artifact was loaded from.
22    pub path: PathBuf,
23    /// Creation bytecode from `bytecode.object`.
24    pub creation_code: Bytes,
25}
26
27impl FoundryArtifact {
28    /// Load a Foundry JSON artifact from disk.
29    ///
30    /// Supports the standard Foundry shape:
31    /// `{ "bytecode": { "object": "0x..." } }`.
32    ///
33    /// The legacy direct string shape `{ "bytecode": "0x..." }` is also
34    /// accepted to make tests and generated artifacts easier to reuse.
35    ///
36    /// # Errors
37    ///
38    /// Returns an error if the file cannot be read, is not valid JSON, lacks a
39    /// usable `bytecode`/`bytecode.object` field, or contains bytecode that is
40    /// empty, not valid hex, or still has unresolved library placeholders (see
41    /// [`load_foundry_creation_code`]).
42    pub fn load(path: impl AsRef<Path>) -> Result<Self> {
43        let path = path.as_ref();
44        let creation_code = load_foundry_creation_code(path)?;
45        Ok(Self {
46            path: path.to_path_buf(),
47            creation_code,
48        })
49    }
50
51    /// Build init code by appending ABI-encoded constructor arguments to the
52    /// artifact's creation bytecode.
53    pub fn init_code(&self, constructor_args: impl AsRef<[u8]>) -> Bytes {
54        build_init_code(&self.creation_code, constructor_args)
55    }
56
57    /// Deploy this artifact into the forked EVM and return the temporary
58    /// deployed address.
59    ///
60    /// `constructor_args` must already be ABI encoded. Use
61    /// [`encode_constructor_args`] for ordinary Solidity constructor tuples.
62    ///
63    /// # Errors
64    ///
65    /// Returns an error if the `CREATE` transaction reverts or halts, or if the
66    /// deployment otherwise fails to produce a deployed address (see
67    /// [`EvmCache::deploy_contract`]).
68    ///
69    /// # Panics
70    ///
71    /// Like any method that may fetch missing state, this must run on a
72    /// multi-thread tokio runtime; deploying on a current-thread runtime panics
73    /// when the fork DB attempts a synchronous RPC fetch.
74    pub fn deploy(
75        &self,
76        cache: &mut EvmCache,
77        deployer: Address,
78        constructor_args: impl AsRef<[u8]>,
79    ) -> Result<Address> {
80        let init_code = self.init_code(constructor_args);
81        let deployed = cache
82            .deploy_contract(deployer, init_code)
83            .with_context(|| format!("deploying Foundry artifact {}", self.path.display()))?;
84        debug!(
85            artifact = %self.path.display(),
86            %deployer,
87            %deployed,
88            "deployed Foundry artifact into EVM cache"
89        );
90        Ok(deployed)
91    }
92
93    /// Deploy this artifact and copy its runtime bytecode to `target`.
94    ///
95    /// This is equivalent to a simulation-friendly `vm.etch` for contracts with
96    /// constructor-initialized immutables: the temporary deployment computes the
97    /// final runtime bytecode, and `target` keeps its existing storage, balance,
98    /// and nonce. `target` must already have non-empty runtime bytecode.
99    ///
100    /// On any error the cache is restored to its pre-deploy snapshot, so a
101    /// failed etch leaves no partial deployment behind.
102    ///
103    /// # Errors
104    ///
105    /// Returns an error if `target` is missing or has no runtime bytecode, if
106    /// the deployment reverts or halts (see [`Self::deploy`]), or if copying the
107    /// runtime bytecode to `target` fails.
108    ///
109    /// # Panics
110    ///
111    /// Must run on a multi-thread tokio runtime; the underlying deployment
112    /// panics on a current-thread runtime when the fork DB attempts a
113    /// synchronous RPC fetch.
114    pub fn etch(
115        &self,
116        cache: &mut EvmCache,
117        target: Address,
118        deployer: Address,
119        constructor_args: impl AsRef<[u8]>,
120    ) -> Result<EtchedContract> {
121        self.etch_with_missing_target_behavior(
122            cache,
123            target,
124            deployer,
125            constructor_args,
126            MissingTargetBehavior::Error,
127        )
128    }
129
130    /// Deploy this artifact and copy its runtime bytecode to `target`, creating
131    /// a default target account when it does not already exist.
132    ///
133    /// Use this only for synthetic simulation addresses where there is no
134    /// storage, balance, or nonce to preserve.
135    ///
136    /// On any error the cache is restored to its pre-deploy snapshot, so a
137    /// failed etch leaves no synthetic target account behind.
138    ///
139    /// # Errors
140    ///
141    /// Returns an error if the deployment reverts or halts (see
142    /// [`Self::deploy`]), or if copying the runtime bytecode to `target` fails
143    /// (for example when the deployed contract has empty runtime bytecode).
144    ///
145    /// # Panics
146    ///
147    /// Must run on a multi-thread tokio runtime; the underlying deployment
148    /// panics on a current-thread runtime when the fork DB attempts a
149    /// synchronous RPC fetch.
150    pub fn etch_or_create(
151        &self,
152        cache: &mut EvmCache,
153        target: Address,
154        deployer: Address,
155        constructor_args: impl AsRef<[u8]>,
156    ) -> Result<EtchedContract> {
157        self.etch_with_missing_target_behavior(
158            cache,
159            target,
160            deployer,
161            constructor_args,
162            MissingTargetBehavior::Create,
163        )
164    }
165
166    fn etch_with_missing_target_behavior(
167        &self,
168        cache: &mut EvmCache,
169        target: Address,
170        deployer: Address,
171        constructor_args: impl AsRef<[u8]>,
172        missing_target: MissingTargetBehavior,
173    ) -> Result<EtchedContract> {
174        let snapshot = cache.snapshot();
175        let result = self.try_etch_with_missing_target_behavior(
176            cache,
177            target,
178            deployer,
179            constructor_args,
180            missing_target,
181        );
182
183        if result.is_err() {
184            cache.restore(snapshot);
185        }
186
187        result
188    }
189
190    fn try_etch_with_missing_target_behavior(
191        &self,
192        cache: &mut EvmCache,
193        target: Address,
194        deployer: Address,
195        constructor_args: impl AsRef<[u8]>,
196        missing_target: MissingTargetBehavior,
197    ) -> Result<EtchedContract> {
198        if matches!(missing_target, MissingTargetBehavior::Error) {
199            cache
200                .require_contract_target(target)
201                .with_context(|| format!("validating target contract {}", target))?;
202        }
203
204        let deployed = self.deploy(cache, deployer, constructor_args)?;
205        cache
206            .override_account_code_with_missing_target(deployed, target, missing_target)
207            .with_context(|| format!("etching runtime bytecode at {}", target))?;
208
209        let code_size = cache
210            .db_mut()
211            .cache
212            .accounts
213            .get(&target)
214            .and_then(|account| account.info.code.as_ref().map(|code| code.len()))
215            .unwrap_or_default();
216
217        info!(
218            artifact = %self.path.display(),
219            %deployed,
220            %target,
221            code_size,
222            "etched Foundry artifact runtime bytecode"
223        );
224
225        Ok(EtchedContract {
226            artifact_path: self.path.clone(),
227            deployed_address: deployed,
228            target_address: target,
229            code_size,
230        })
231    }
232}
233
234/// Result of deploying an artifact and etching its runtime code at a target.
235#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct EtchedContract {
237    /// Artifact path used for the deployment.
238    pub artifact_path: PathBuf,
239    /// Temporary address returned by the CREATE deployment.
240    pub deployed_address: Address,
241    /// Address whose runtime bytecode was replaced.
242    pub target_address: Address,
243    /// Runtime bytecode size at `target` after etching.
244    pub code_size: usize,
245}
246
247/// ABI-encode constructor arguments.
248///
249/// Pass a tuple of alloy Solidity values matching the constructor parameter
250/// list, e.g. `(owner, weth, vault)`. Single-argument constructors need a
251/// trailing comma so the value is still a tuple: `(owner,)`. An empty tuple
252/// `()` encodes to empty bytes, which is correct for argument-less
253/// constructors.
254///
255/// The encoding mirrors Solidity constructor parameter encoding
256/// (`abi.encode(arg0, arg1, ...)`): it uses [`SolValue::abi_encode_params`],
257/// which lays the arguments out as a flat parameter list. This differs from
258/// [`SolValue::abi_encode`], which would wrap a tuple in an extra layer
259/// (matching `abi.encode((...))`) and produce the wrong bytes for a
260/// constructor.
261///
262/// The trait bounds spell out "any alloy Solidity value tuple": `T: SolValue`
263/// means each element implements the alloy Solidity-value trait, and the
264/// `TokenSeq` bound on `T::SolType` requires the tuple's token to be a
265/// sequence so it can be encoded as a parameter list. In practice you do not
266/// construct these bounds yourself — they are satisfied automatically by
267/// tuples of alloy primitives such as [`Address`], [`U256`](alloy_primitives::U256),
268/// and `String`.
269///
270/// ```ignore
271/// let args = evm_fork_cache::deploy::encode_constructor_args((owner, weth, vault));
272/// ```
273pub fn encode_constructor_args<T>(args: T) -> Bytes
274where
275    T: SolValue,
276    for<'a> <T::SolType as SolType>::Token<'a>: TokenSeq<'a>,
277{
278    args.abi_encode_params().into()
279}
280
281/// Load creation bytecode from a Foundry artifact.
282///
283/// Reads the JSON at `path` and decodes the creation bytecode from
284/// `bytecode.object` (or the legacy direct-string `bytecode` field).
285///
286/// # Errors
287///
288/// Returns an error when:
289/// - the file cannot be read,
290/// - the contents are not valid JSON,
291/// - the JSON has no `bytecode` field, or `bytecode` has neither an `object`
292///   string nor a direct string value,
293/// - the bytecode hex is empty, still contains unresolved library
294///   placeholders (`__$...$__`), or is otherwise not valid hex.
295pub fn load_foundry_creation_code(path: impl AsRef<Path>) -> Result<Bytes> {
296    let path = path.as_ref();
297    let content = std::fs::read_to_string(path)
298        .with_context(|| format!("failed to read Foundry artifact at {}", path.display()))?;
299    let json: serde_json::Value = serde_json::from_str(&content).with_context(|| {
300        format!(
301            "failed to parse Foundry artifact JSON at {}",
302            path.display()
303        )
304    })?;
305
306    let bytecode = json
307        .get("bytecode")
308        .ok_or_else(|| anyhow::anyhow!("artifact {} has no `bytecode` field", path.display()))?;
309
310    let bytecode_hex = bytecode
311        .get("object")
312        .and_then(serde_json::Value::as_str)
313        .or_else(|| bytecode.as_str())
314        .ok_or_else(|| {
315            anyhow::anyhow!(
316                "artifact {} has no `bytecode.object` string",
317                path.display()
318            )
319        })?;
320
321    decode_hex_bytecode(bytecode_hex)
322        .with_context(|| format!("failed to decode bytecode in {}", path.display()))
323}
324
325/// Build init code from creation bytecode and ABI-encoded constructor args.
326///
327/// Init code is simply the contract's creation bytecode with the ABI-encoded
328/// constructor arguments appended, matching how the EVM expects a `CREATE`
329/// payload to be laid out. The `constructor_args` must already be ABI encoded;
330/// use [`encode_constructor_args`] to produce them from an alloy Solidity
331/// value tuple.
332///
333/// ```
334/// use evm_fork_cache::deploy::build_init_code;
335///
336/// let init = build_init_code([0x60, 0x80], [0x01, 0x02, 0x03]);
337/// assert_eq!(init.as_ref(), &[0x60, 0x80, 0x01, 0x02, 0x03]);
338/// ```
339pub fn build_init_code(
340    creation_code: impl AsRef<[u8]>,
341    constructor_args: impl AsRef<[u8]>,
342) -> Bytes {
343    let creation_code = creation_code.as_ref();
344    let constructor_args = constructor_args.as_ref();
345    let mut init_code = Vec::with_capacity(creation_code.len() + constructor_args.len());
346    init_code.extend_from_slice(creation_code);
347    init_code.extend_from_slice(constructor_args);
348    Bytes::from(init_code)
349}
350
351/// Deploy a Foundry artifact into the forked EVM and return its temporary
352/// deployed address.
353///
354/// # Errors
355///
356/// Returns an error if the artifact cannot be loaded (see
357/// [`load_foundry_creation_code`]) or if the deployment reverts or halts (see
358/// [`FoundryArtifact::deploy`]).
359///
360/// # Panics
361///
362/// Must run on a multi-thread tokio runtime; the deployment panics on a
363/// current-thread runtime when the fork DB attempts a synchronous RPC fetch.
364pub fn deploy_foundry_artifact(
365    cache: &mut EvmCache,
366    artifact_path: impl AsRef<Path>,
367    deployer: Address,
368    constructor_args: impl AsRef<[u8]>,
369) -> Result<Address> {
370    FoundryArtifact::load(artifact_path)?.deploy(cache, deployer, constructor_args)
371}
372
373/// Deploy a Foundry artifact and copy its runtime bytecode to `target`.
374///
375/// `target` must already have non-empty runtime bytecode. Its storage, balance,
376/// and nonce are preserved. If `target` is missing or has no runtime bytecode,
377/// this returns an error. Use [`etch_foundry_artifact_or_create`] for synthetic
378/// simulation addresses.
379///
380/// # Errors
381///
382/// Returns an error if the artifact cannot be loaded (see
383/// [`load_foundry_creation_code`]), if `target` is missing or has no runtime
384/// bytecode, if the deployment reverts or halts, or if copying the runtime
385/// bytecode to `target` fails (see [`FoundryArtifact::etch`]).
386///
387/// # Panics
388///
389/// Must run on a multi-thread tokio runtime; the deployment panics on a
390/// current-thread runtime when the fork DB attempts a synchronous RPC fetch.
391pub fn etch_foundry_artifact(
392    cache: &mut EvmCache,
393    target: Address,
394    artifact_path: impl AsRef<Path>,
395    deployer: Address,
396    constructor_args: impl AsRef<[u8]>,
397) -> Result<EtchedContract> {
398    FoundryArtifact::load(artifact_path)?.etch(cache, target, deployer, constructor_args)
399}
400
401/// Deploy a Foundry artifact and copy its runtime bytecode to `target`, creating
402/// a default target account when it does not already exist.
403///
404/// Prefer [`etch_foundry_artifact`] for forked/live contract addresses whose
405/// storage, balance, or nonce should be preserved.
406///
407/// # Errors
408///
409/// Returns an error if the artifact cannot be loaded (see
410/// [`load_foundry_creation_code`]), if the deployment reverts or halts, or if
411/// copying the runtime bytecode to `target` fails, for example when the
412/// deployed contract has empty runtime bytecode (see
413/// [`FoundryArtifact::etch_or_create`]).
414///
415/// # Panics
416///
417/// Must run on a multi-thread tokio runtime; the deployment panics on a
418/// current-thread runtime when the fork DB attempts a synchronous RPC fetch.
419pub fn etch_foundry_artifact_or_create(
420    cache: &mut EvmCache,
421    target: Address,
422    artifact_path: impl AsRef<Path>,
423    deployer: Address,
424    constructor_args: impl AsRef<[u8]>,
425) -> Result<EtchedContract> {
426    FoundryArtifact::load(artifact_path)?.etch_or_create(cache, target, deployer, constructor_args)
427}
428
429fn decode_hex_bytecode(bytecode_hex: &str) -> Result<Bytes> {
430    let stripped = bytecode_hex.strip_prefix("0x").unwrap_or(bytecode_hex);
431    if stripped.is_empty() {
432        bail!("empty bytecode");
433    }
434    if stripped.contains("__") {
435        bail!("bytecode contains unresolved library placeholders");
436    }
437
438    alloy_primitives::hex::decode(stripped)
439        .map(Bytes::from)
440        .context("invalid hex bytecode")
441}
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446    use alloy_primitives::U256;
447    use revm::state::{AccountInfo, Bytecode};
448    use std::{
449        sync::Arc,
450        time::{SystemTime, UNIX_EPOCH},
451    };
452
453    #[test]
454    fn build_init_code_appends_constructor_args() {
455        let init = build_init_code([0x60, 0x80], [0x01, 0x02, 0x03]);
456        assert_eq!(init.as_ref(), &[0x60, 0x80, 0x01, 0x02, 0x03]);
457    }
458
459    #[test]
460    fn encode_constructor_args_uses_parameter_encoding_for_dynamic_args() {
461        let args = (String::from("hello"), U256::from(7));
462        let encoded = encode_constructor_args(args.clone());
463
464        assert_eq!(encoded.as_ref(), args.abi_encode_params().as_slice());
465        assert_ne!(encoded.as_ref(), args.abi_encode().as_slice());
466    }
467
468    #[test]
469    fn encode_constructor_args_empty_tuple_is_empty() {
470        assert!(encode_constructor_args(()).is_empty());
471    }
472
473    #[test]
474    fn strict_override_rejects_known_empty_target() -> Result<()> {
475        let mut cache = setup_cache();
476        let source = Address::repeat_byte(0x11);
477        let target = Address::repeat_byte(0x22);
478
479        cache
480            .db_mut()
481            .insert_account_info(source, account_with_runtime(&[0x00], U256::ZERO, 1));
482        cache
483            .db_mut()
484            .insert_account_info(target, AccountInfo::default());
485
486        let err = cache.override_account_code(source, target).unwrap_err();
487        assert!(err.to_string().contains("target account"));
488        assert!(err.to_string().contains("no runtime bytecode"));
489
490        let target_account = cache
491            .db_mut()
492            .cache
493            .accounts
494            .get(&target)
495            .expect("target should still exist");
496        assert!(
497            target_account
498                .info
499                .code
500                .as_ref()
501                .is_none_or(|code| code.is_empty())
502        );
503
504        cache.override_or_create_account_code(source, target)?;
505        let target_account = cache
506            .db_mut()
507            .cache
508            .accounts
509            .get(&target)
510            .expect("explicit create should write target code");
511        assert!(
512            target_account
513                .info
514                .code
515                .as_ref()
516                .is_some_and(|code| !code.is_empty())
517        );
518
519        Ok(())
520    }
521
522    #[test]
523    fn strict_etch_rejects_empty_target_before_deploying() -> Result<()> {
524        let mut cache = setup_cache();
525        let deployer = Address::ZERO;
526        let target = Address::repeat_byte(0x33);
527        let create_address = zero_nonce_create_address()?;
528        let artifact = memory_artifact(non_empty_runtime_creation_code());
529
530        cache
531            .db_mut()
532            .insert_account_info(deployer, AccountInfo::default());
533        cache
534            .db_mut()
535            .insert_account_info(create_address, AccountInfo::default());
536        cache
537            .db_mut()
538            .insert_account_info(target, AccountInfo::default());
539
540        let err = artifact
541            .etch(&mut cache, target, deployer, Bytes::new())
542            .unwrap_err();
543        let err = format!("{err:#}");
544        assert!(err.contains("validating target contract"));
545        assert!(err.contains("no runtime bytecode"));
546
547        assert_eq!(cached_nonce(&mut cache, deployer), Some(0));
548        assert_eq!(cached_code_len(&mut cache, create_address), Some(0));
549
550        Ok(())
551    }
552
553    #[test]
554    fn etch_restores_cache_when_override_fails_after_deploy() -> Result<()> {
555        let mut cache = setup_cache();
556        let deployer = Address::ZERO;
557        let target = Address::repeat_byte(0x44);
558        let create_address = zero_nonce_create_address()?;
559        let artifact = memory_artifact(empty_runtime_creation_code());
560
561        cache
562            .db_mut()
563            .insert_account_info(deployer, AccountInfo::default());
564        cache
565            .db_mut()
566            .insert_account_info(create_address, AccountInfo::default());
567
568        let err = artifact
569            .etch_or_create(&mut cache, target, deployer, Bytes::new())
570            .unwrap_err();
571        assert!(err.to_string().contains("etching runtime bytecode"));
572        assert!(err.to_string().contains("bytecode"));
573
574        assert_eq!(cached_nonce(&mut cache, deployer), Some(0));
575        assert_eq!(cached_code_len(&mut cache, create_address), Some(0));
576        assert!(
577            !cache.db_mut().cache.accounts.contains_key(&target),
578            "failed etch should not leave a synthetic target account behind"
579        );
580
581        Ok(())
582    }
583
584    #[test]
585    fn decode_hex_bytecode_rejects_unlinked_libraries() {
586        let err = decode_hex_bytecode("0x60__$abc$__").unwrap_err();
587        assert!(err.to_string().contains("unresolved library"));
588    }
589
590    #[test]
591    fn load_foundry_creation_code_reads_bytecode_object() -> Result<()> {
592        let path = temp_artifact_path("foundry-bytecode-object");
593        std::fs::write(&path, r#"{"bytecode":{"object":"0x60016002"}}"#)?;
594
595        let code = load_foundry_creation_code(&path)?;
596
597        std::fs::remove_file(&path).ok();
598        assert_eq!(code.as_ref(), &[0x60, 0x01, 0x60, 0x02]);
599        Ok(())
600    }
601
602    #[test]
603    fn load_foundry_creation_code_accepts_direct_string_bytecode() -> Result<()> {
604        let path = temp_artifact_path("foundry-bytecode-string");
605        std::fs::write(&path, r#"{"bytecode":"0x6001"}"#)?;
606
607        let code = load_foundry_creation_code(&path)?;
608
609        std::fs::remove_file(&path).ok();
610        assert_eq!(code.as_ref(), &[0x60, 0x01]);
611        Ok(())
612    }
613
614    fn temp_artifact_path(prefix: &str) -> PathBuf {
615        let nanos = SystemTime::now()
616            .duration_since(UNIX_EPOCH)
617            .expect("system time before unix epoch")
618            .as_nanos();
619        std::env::temp_dir().join(format!("{prefix}-{}-{nanos}.json", std::process::id()))
620    }
621
622    fn setup_cache() -> EvmCache {
623        use alloy_provider::{RootProvider, network::AnyNetwork};
624        use alloy_rpc_client::RpcClient;
625        use alloy_transport::mock::Asserter;
626
627        let asserter = Asserter::new();
628        let client = RpcClient::mocked(asserter);
629        let provider = RootProvider::<AnyNetwork>::new(client);
630
631        let rt = tokio::runtime::Builder::new_current_thread()
632            .enable_all()
633            .build()
634            .expect("runtime should build");
635
636        rt.block_on(EvmCache::new(Arc::new(provider)))
637    }
638
639    fn memory_artifact(creation_code: Bytes) -> FoundryArtifact {
640        FoundryArtifact {
641            path: PathBuf::from("memory-artifact.json"),
642            creation_code,
643        }
644    }
645
646    fn account_with_runtime(runtime: &[u8], balance: U256, nonce: u64) -> AccountInfo {
647        let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(runtime));
648        let code_hash = bytecode.hash_slow();
649        AccountInfo {
650            balance,
651            nonce,
652            code: Some(bytecode),
653            code_hash,
654            account_id: None,
655        }
656    }
657
658    fn non_empty_runtime_creation_code() -> Bytes {
659        Bytes::from_static(&[
660            0x60, 0x01, // PUSH1 1 byte runtime
661            0x60, 0x0c, // PUSH1 runtime offset
662            0x60, 0x00, // PUSH1 destination offset
663            0x39, // CODECOPY
664            0x60, 0x01, // PUSH1 1 byte runtime
665            0x60, 0x00, // PUSH1 return offset
666            0xf3, // RETURN
667            0x00, // runtime: STOP
668        ])
669    }
670
671    fn empty_runtime_creation_code() -> Bytes {
672        Bytes::from_static(&[
673            0x60, 0x00, // PUSH1 0 byte runtime
674            0x60, 0x00, // PUSH1 return offset
675            0xf3, // RETURN
676        ])
677    }
678
679    fn zero_nonce_create_address() -> Result<Address> {
680        "0xbd770416a3345f91e4b34576cb804a576fa48eb1"
681            .parse()
682            .context("zero nonce CREATE address should parse")
683    }
684
685    fn cached_nonce(cache: &mut EvmCache, address: Address) -> Option<u64> {
686        cache
687            .db_mut()
688            .cache
689            .accounts
690            .get(&address)
691            .map(|account| account.info.nonce)
692    }
693
694    fn cached_code_len(cache: &mut EvmCache, address: Address) -> Option<usize> {
695        cache
696            .db_mut()
697            .cache
698            .accounts
699            .get(&address)
700            .map(|account| account.info.code.as_ref().map_or(0, |code| code.len()))
701    }
702}