1use crate::{
4    serde_helpers,
5    sourcemap::{self, SourceMap, SyntaxError},
6    FunctionDebugData, GeneratedSource, Offsets,
7};
8use alloy_primitives::{hex, Address, Bytes};
9use foundry_compilers_core::utils;
10use serde::{Deserialize, Serialize, Serializer};
11use std::collections::BTreeMap;
12
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct Bytecode {
16    #[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
18    pub function_debug_data: BTreeMap<String, FunctionDebugData>,
19    #[serde(serialize_with = "serialize_bytecode_without_prefix")]
21    pub object: BytecodeObject,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
24    pub opcodes: Option<String>,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub source_map: Option<String>,
28    #[serde(default, skip_serializing_if = "Vec::is_empty")]
31    pub generated_sources: Vec<GeneratedSource>,
32    #[serde(default)]
34    pub link_references: BTreeMap<String, BTreeMap<String, Vec<Offsets>>>,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub struct CompactBytecode {
40    pub object: BytecodeObject,
42    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub source_map: Option<String>,
45    #[serde(default)]
47    pub link_references: BTreeMap<String, BTreeMap<String, Vec<Offsets>>>,
48}
49
50impl CompactBytecode {
51    pub fn empty() -> Self {
54        Self { object: Default::default(), source_map: None, link_references: Default::default() }
55    }
56
57    pub fn source_map(&self) -> Option<Result<SourceMap, SyntaxError>> {
61        self.source_map.as_ref().map(|map| sourcemap::parse(map))
62    }
63
64    pub fn link(&mut self, file: &str, library: &str, address: Address) -> bool {
70        if !self.object.is_unlinked() {
71            return true;
72        }
73
74        if let Some((key, mut contracts)) = self.link_references.remove_entry(file) {
75            if contracts.remove(library).is_some() {
76                self.object.link(file, library, address);
77            }
78            if !contracts.is_empty() {
79                self.link_references.insert(key, contracts);
80            }
81            if self.link_references.is_empty() {
82                return self.object.resolve().is_some();
83            }
84        }
85        false
86    }
87
88    pub fn bytes(&self) -> Option<&Bytes> {
90        self.object.as_bytes()
91    }
92
93    pub fn into_bytes(self) -> Option<Bytes> {
95        self.object.into_bytes()
96    }
97}
98
99impl From<Bytecode> for CompactBytecode {
100    fn from(bcode: Bytecode) -> Self {
101        Self {
102            object: bcode.object,
103            source_map: bcode.source_map,
104            link_references: bcode.link_references,
105        }
106    }
107}
108
109impl From<CompactBytecode> for Bytecode {
110    fn from(bcode: CompactBytecode) -> Self {
111        Self {
112            object: bcode.object,
113            source_map: bcode.source_map,
114            link_references: bcode.link_references,
115            function_debug_data: Default::default(),
116            opcodes: Default::default(),
117            generated_sources: Default::default(),
118        }
119    }
120}
121
122impl From<BytecodeObject> for Bytecode {
123    fn from(object: BytecodeObject) -> Self {
124        Self {
125            object,
126            function_debug_data: Default::default(),
127            opcodes: Default::default(),
128            source_map: Default::default(),
129            generated_sources: Default::default(),
130            link_references: Default::default(),
131        }
132    }
133}
134
135impl Bytecode {
136    pub fn source_map(&self) -> Option<Result<SourceMap, SyntaxError>> {
140        self.source_map.as_ref().map(|map| sourcemap::parse(map))
141    }
142
143    pub fn link_fully_qualified(&mut self, name: &str, addr: Address) -> bool {
145        if let Some((file, lib)) = name.split_once(':') {
146            self.link(file, lib, addr)
147        } else {
148            false
149        }
150    }
151
152    pub fn link(&mut self, file: &str, library: &str, address: Address) -> bool {
158        if !self.object.is_unlinked() {
159            return true;
160        }
161
162        if let Some((key, mut contracts)) = self.link_references.remove_entry(file) {
163            if contracts.remove(library).is_some() {
164                self.object.link(file, library, address);
165            }
166            if !contracts.is_empty() {
167                self.link_references.insert(key, contracts);
168            }
169            if self.link_references.is_empty() {
170                return self.object.resolve().is_some();
171            }
172        }
173        false
174    }
175
176    pub fn link_all<I, S, T>(&mut self, libs: I) -> bool
178    where
179        I: IntoIterator<Item = (S, T, Address)>,
180        S: AsRef<str>,
181        T: AsRef<str>,
182    {
183        for (file, lib, addr) in libs.into_iter() {
184            if self.link(file.as_ref(), lib.as_ref(), addr) {
185                return true;
186            }
187        }
188        false
189    }
190
191    pub fn link_all_fully_qualified<I, S>(&mut self, libs: I) -> bool
193    where
194        I: IntoIterator<Item = (S, Address)>,
195        S: AsRef<str>,
196    {
197        for (name, addr) in libs.into_iter() {
198            if self.link_fully_qualified(name.as_ref(), addr) {
199                return true;
200            }
201        }
202        false
203    }
204
205    pub fn bytes(&self) -> Option<&Bytes> {
207        self.object.as_bytes()
208    }
209
210    pub fn into_bytes(self) -> Option<Bytes> {
212        self.object.into_bytes()
213    }
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
218#[serde(untagged)]
219pub enum BytecodeObject {
220    #[serde(deserialize_with = "serde_helpers::deserialize_bytes")]
222    Bytecode(Bytes),
223    #[serde(with = "serde_helpers::string_bytes")]
225    Unlinked(String),
226}
227
228impl BytecodeObject {
229    pub fn as_bytes(&self) -> Option<&Bytes> {
231        match self {
232            Self::Bytecode(bytes) => Some(bytes),
233            Self::Unlinked(_) => None,
234        }
235    }
236
237    pub fn into_bytes(self) -> Option<Bytes> {
239        match self {
240            Self::Bytecode(bytes) => Some(bytes),
241            Self::Unlinked(_) => None,
242        }
243    }
244
245    pub fn bytes_len(&self) -> usize {
249        self.as_bytes().map(|b| b.as_ref().len()).unwrap_or_default()
250    }
251
252    pub fn as_str(&self) -> Option<&str> {
254        match self {
255            Self::Bytecode(_) => None,
256            Self::Unlinked(s) => Some(s.as_str()),
257        }
258    }
259
260    pub fn into_unlinked(self) -> Option<String> {
262        match self {
263            Self::Bytecode(_) => None,
264            Self::Unlinked(code) => Some(code),
265        }
266    }
267
268    pub fn is_unlinked(&self) -> bool {
270        matches!(self, Self::Unlinked(_))
271    }
272
273    pub fn is_bytecode(&self) -> bool {
275        matches!(self, Self::Bytecode(_))
276    }
277
278    pub fn is_non_empty_bytecode(&self) -> bool {
282        self.as_bytes().map(|c| !c.0.is_empty()).unwrap_or_default()
283    }
284
285    pub fn resolve(&mut self) -> Option<&Bytes> {
289        if let Self::Unlinked(unlinked) = self {
290            if let Ok(linked) = hex::decode(unlinked) {
291                *self = Self::Bytecode(linked.into());
292            }
293        }
294        self.as_bytes()
295    }
296
297    pub fn link_fully_qualified(&mut self, name: &str, addr: Address) -> &mut Self {
306        if let Self::Unlinked(unlinked) = self {
307            let place_holder = utils::library_hash_placeholder(name);
308            let hex_addr = hex::encode(addr);
310
311            let fully_qualified_placeholder = utils::library_fully_qualified_placeholder(name);
314
315            *unlinked = unlinked
316                .replace(&format!("__{fully_qualified_placeholder}__"), &hex_addr)
317                .replace(&format!("__{place_holder}__"), &hex_addr)
318        }
319        self
320    }
321
322    pub fn link(&mut self, file: &str, library: &str, addr: Address) -> &mut Self {
326        self.link_fully_qualified(&format!("{file}:{library}"), addr)
327    }
328
329    pub fn link_all<I, S, T>(&mut self, libs: I) -> &mut Self
331    where
332        I: IntoIterator<Item = (S, T, Address)>,
333        S: AsRef<str>,
334        T: AsRef<str>,
335    {
336        for (file, lib, addr) in libs.into_iter() {
337            self.link(file.as_ref(), lib.as_ref(), addr);
338        }
339        self
340    }
341
342    pub fn contains_fully_qualified_placeholder(&self, name: &str) -> bool {
344        if let Self::Unlinked(unlinked) = self {
345            unlinked.contains(&utils::library_hash_placeholder(name))
346                || unlinked.contains(&utils::library_fully_qualified_placeholder(name))
347        } else {
348            false
349        }
350    }
351
352    pub fn contains_placeholder(&self, file: &str, library: &str) -> bool {
354        self.contains_fully_qualified_placeholder(&format!("{file}:{library}"))
355    }
356
357    pub fn strip_bytecode_placeholders(&self) -> Option<Bytes> {
362        match &self {
363            Self::Bytecode(bytes) => Some(bytes.clone()),
364            Self::Unlinked(s) => {
365                let bytes = replace_placeholders_and_decode(s).ok()?;
367                Some(bytes.into())
368            }
369        }
370    }
371}
372
373impl Default for BytecodeObject {
375    fn default() -> Self {
376        Self::Bytecode(Default::default())
377    }
378}
379
380impl AsRef<[u8]> for BytecodeObject {
381    fn as_ref(&self) -> &[u8] {
382        match self {
383            Self::Bytecode(code) => code.as_ref(),
384            Self::Unlinked(code) => code.as_bytes(),
385        }
386    }
387}
388
389pub fn serialize_bytecode_without_prefix<S>(
394    bytecode: &BytecodeObject,
395    s: S,
396) -> Result<S::Ok, S::Error>
397where
398    S: Serializer,
399{
400    match bytecode {
401        BytecodeObject::Bytecode(code) => s.serialize_str(&hex::encode(code)),
402        BytecodeObject::Unlinked(code) => s.serialize_str(code.strip_prefix("0x").unwrap_or(code)),
403    }
404}
405
406pub fn replace_placeholders_and_decode(s: &str) -> Result<Vec<u8>, hex::FromHexError> {
408    let re = regex::Regex::new(r"_\$.{34}\$_").expect("invalid regex");
409    let s = re.replace_all(s, "00".repeat(40));
410    hex::decode(s.as_bytes())
411}
412
413#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
414pub struct DeployedBytecode {
415    #[serde(flatten)]
416    pub bytecode: Option<Bytecode>,
417    #[serde(
418        default,
419        rename = "immutableReferences",
420        skip_serializing_if = "::std::collections::BTreeMap::is_empty"
421    )]
422    pub immutable_references: BTreeMap<String, Vec<Offsets>>,
423}
424
425impl DeployedBytecode {
426    pub fn bytes(&self) -> Option<&Bytes> {
428        self.bytecode.as_ref().and_then(|bytecode| bytecode.object.as_bytes())
429    }
430
431    pub fn into_bytes(self) -> Option<Bytes> {
433        self.bytecode.and_then(|bytecode| bytecode.object.into_bytes())
434    }
435}
436
437impl From<Bytecode> for DeployedBytecode {
438    fn from(bcode: Bytecode) -> Self {
439        Self { bytecode: Some(bcode), immutable_references: Default::default() }
440    }
441}
442
443#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
444#[serde(rename_all = "camelCase")]
445pub struct CompactDeployedBytecode {
446    #[serde(flatten)]
447    pub bytecode: Option<CompactBytecode>,
448    #[serde(
449        default,
450        rename = "immutableReferences",
451        skip_serializing_if = "::std::collections::BTreeMap::is_empty"
452    )]
453    pub immutable_references: BTreeMap<String, Vec<Offsets>>,
454}
455
456impl CompactDeployedBytecode {
457    pub fn empty() -> Self {
460        Self { bytecode: Some(CompactBytecode::empty()), immutable_references: Default::default() }
461    }
462
463    pub fn bytes(&self) -> Option<&Bytes> {
465        self.bytecode.as_ref().and_then(|bytecode| bytecode.object.as_bytes())
466    }
467
468    pub fn into_bytes(self) -> Option<Bytes> {
470        self.bytecode.and_then(|bytecode| bytecode.object.into_bytes())
471    }
472
473    pub fn source_map(&self) -> Option<Result<SourceMap, SyntaxError>> {
477        self.bytecode.as_ref().and_then(|bytecode| bytecode.source_map())
478    }
479}
480
481impl From<DeployedBytecode> for CompactDeployedBytecode {
482    fn from(bcode: DeployedBytecode) -> Self {
483        Self {
484            bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()),
485            immutable_references: bcode.immutable_references,
486        }
487    }
488}
489
490impl From<CompactDeployedBytecode> for DeployedBytecode {
491    fn from(bcode: CompactDeployedBytecode) -> Self {
492        Self {
493            bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()),
494            immutable_references: bcode.immutable_references,
495        }
496    }
497}
498
499#[cfg(test)]
500mod tests {
501    use crate::{ConfigurableContractArtifact, ContractBytecode};
502
503    #[test]
504    fn test_empty_bytecode() {
505        let empty = r#"
506        {
507  "abi": [],
508  "bytecode": {
509    "object": "0x",
510    "linkReferences": {}
511  },
512  "deployedBytecode": {
513    "object": "0x",
514    "linkReferences": {}
515  }
516  }
517        "#;
518
519        let artifact: ConfigurableContractArtifact = serde_json::from_str(empty).unwrap();
520        let contract = artifact.into_contract_bytecode();
521        let bytecode: ContractBytecode = contract.into();
522        let bytecode = bytecode.unwrap();
523        assert!(!bytecode.bytecode.object.is_unlinked());
524    }
525}