1use alloy_primitives::{B256, Bytes, U256, hex, keccak256};
4use foundry_compilers::{
5 Project,
6 artifacts::{BytecodeObject, SolcLanguage},
7 error::SolcError,
8 flatten::{Flattener, FlattenerError},
9};
10use regex::Regex;
11use std::{path::Path, sync::LazyLock};
12
13static BYTECODE_PLACEHOLDER_RE: LazyLock<Regex> =
14 LazyLock::new(|| Regex::new(r"__\$.{34}\$__").expect("invalid regex"));
15
16pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
18 block_on_handle(&tokio::runtime::Handle::current(), future)
19}
20
21pub fn block_on_handle<F: std::future::Future>(
23 handle: &tokio::runtime::Handle,
24 future: F,
25) -> F::Output {
26 tokio::task::block_in_place(|| handle.block_on(future))
27}
28
29pub fn erc7201(id: &str) -> B256 {
49 let x = Into::<U256>::into(keccak256(id)) - U256::ONE;
50 keccak256(x.to_be_bytes::<32>()) & B256::from(!U256::from(0xff))
51}
52
53pub fn find_metadata_start(bytecode: &[u8]) -> Option<usize> {
56 let (rest, metadata_len_bytes) = bytecode.split_last_chunk()?;
58 let metadata_len = u16::from_be_bytes(*metadata_len_bytes) as usize;
59 if metadata_len > rest.len() {
60 return None;
61 }
62 ciborium::from_reader::<ciborium::Value, _>(&rest[rest.len() - metadata_len..])
63 .is_ok()
64 .then(|| rest.len() - metadata_len)
65}
66
67pub fn ignore_metadata_hash(bytecode: &[u8]) -> &[u8] {
70 if let Some(metadata) = find_metadata_start(bytecode) {
71 &bytecode[..metadata]
72 } else {
73 bytecode
74 }
75}
76
77pub fn has_metadata_hash(bytecode: &[u8]) -> bool {
80 let Some(start) = find_metadata_start(bytecode) else { return false };
81 let Ok(ciborium::Value::Map(entries)) =
82 ciborium::from_reader::<ciborium::Value, _>(&bytecode[start..bytecode.len() - 2])
83 else {
84 return false;
85 };
86 entries.iter().any(|(key, _)| matches!(key.as_text(), Some("ipfs" | "bzzr0" | "bzzr1")))
87}
88
89pub fn strip_bytecode_placeholders(bytecode: &BytecodeObject) -> Option<Bytes> {
94 match &bytecode {
95 BytecodeObject::Bytecode(bytes) => Some(bytes.clone()),
96 BytecodeObject::Unlinked(s) => {
97 let s = (*BYTECODE_PLACEHOLDER_RE).replace_all(s, "00".repeat(20));
99 let bytes = hex::decode(s.as_bytes());
100 Some(bytes.ok()?.into())
101 }
102 }
103}
104
105pub fn flatten(project: Project, target_path: &Path) -> eyre::Result<String> {
109 let paths = project.paths.clone();
111 let flattened = match Flattener::new(project, target_path) {
112 Ok(flattener) => Ok(flattener.flatten()),
113 Err(FlattenerError::Compilation(_)) => {
114 paths.with_language::<SolcLanguage>().flatten(target_path)
115 }
116 Err(FlattenerError::Other(err)) => Err(err),
117 }
118 .map_err(|err: SolcError| eyre::eyre!("Failed to flatten: {err}"))?;
119
120 Ok(flattened)
121}