diff --git a/crates/artifacts/solc/Cargo.toml b/crates/artifacts/solc/Cargo.toml index 807b2f798..15f0f1240 100644 --- a/crates/artifacts/solc/Cargo.toml +++ b/crates/artifacts/solc/Cargo.toml @@ -25,6 +25,7 @@ serde.workspace = true thiserror.workspace = true tracing.workspace = true yansi.workspace = true +regex.workspace = true # async tokio = { workspace = true, optional = true, features = ["fs"] } diff --git a/crates/artifacts/solc/src/bytecode.rs b/crates/artifacts/solc/src/bytecode.rs index eb6985ff0..b88121d9e 100644 --- a/crates/artifacts/solc/src/bytecode.rs +++ b/crates/artifacts/solc/src/bytecode.rs @@ -353,6 +353,21 @@ impl BytecodeObject { pub fn contains_placeholder(&self, file: &str, library: &str) -> bool { self.contains_fully_qualified_placeholder(&format!("{file}:{library}")) } + + /// Strips all __$xxx$__ placeholders from the bytecode if it's an unlinked bytecode. + /// by replacing them with 20 zero bytes. + /// This is useful for matching bytecodes to a contract source, and for the source map, + /// in which the actual address of the placeholder isn't important. + pub fn strip_bytecode_placeholders(&self) -> Option { + match &self { + Self::Bytecode(bytes) => Some(bytes.clone()), + Self::Unlinked(s) => { + // Replace all __$xxx$__ placeholders with 32 zero bytes + let bytes = replace_placeholders_and_decode(s).ok()?; + Some(bytes.into()) + } + } + } } // Returns an empty bytecode object @@ -388,6 +403,13 @@ where } } +// Replace all __$xxx$__ placeholders with 32 zero bytes +pub fn replace_placeholders_and_decode(s: &str) -> Result, hex::FromHexError> { + let re = regex::Regex::new(r"_\$.{34}\$_").expect("invalid regex"); + let s = re.replace_all(s, "00".repeat(40)); + hex::decode(s.as_bytes()) +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct DeployedBytecode { #[serde(flatten)]