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