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
358impl Default for BytecodeObject {
360    fn default() -> Self {
361        Self::Bytecode(Default::default())
362    }
363}
364
365impl AsRef<[u8]> for BytecodeObject {
366    fn as_ref(&self) -> &[u8] {
367        match self {
368            Self::Bytecode(code) => code.as_ref(),
369            Self::Unlinked(code) => code.as_bytes(),
370        }
371    }
372}
373
374pub fn serialize_bytecode_without_prefix<S>(
379    bytecode: &BytecodeObject,
380    s: S,
381) -> Result<S::Ok, S::Error>
382where
383    S: Serializer,
384{
385    match bytecode {
386        BytecodeObject::Bytecode(code) => s.serialize_str(&hex::encode(code)),
387        BytecodeObject::Unlinked(code) => s.serialize_str(code.strip_prefix("0x").unwrap_or(code)),
388    }
389}
390
391#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
392pub struct DeployedBytecode {
393    #[serde(flatten)]
394    pub bytecode: Option<Bytecode>,
395    #[serde(
396        default,
397        rename = "immutableReferences",
398        skip_serializing_if = "::std::collections::BTreeMap::is_empty"
399    )]
400    pub immutable_references: BTreeMap<String, Vec<Offsets>>,
401}
402
403impl DeployedBytecode {
404    pub fn bytes(&self) -> Option<&Bytes> {
406        self.bytecode.as_ref().and_then(|bytecode| bytecode.object.as_bytes())
407    }
408
409    pub fn into_bytes(self) -> Option<Bytes> {
411        self.bytecode.and_then(|bytecode| bytecode.object.into_bytes())
412    }
413}
414
415impl From<Bytecode> for DeployedBytecode {
416    fn from(bcode: Bytecode) -> Self {
417        Self { bytecode: Some(bcode), immutable_references: Default::default() }
418    }
419}
420
421#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
422#[serde(rename_all = "camelCase")]
423pub struct CompactDeployedBytecode {
424    #[serde(flatten)]
425    pub bytecode: Option<CompactBytecode>,
426    #[serde(
427        default,
428        rename = "immutableReferences",
429        skip_serializing_if = "::std::collections::BTreeMap::is_empty"
430    )]
431    pub immutable_references: BTreeMap<String, Vec<Offsets>>,
432}
433
434impl CompactDeployedBytecode {
435    pub fn empty() -> Self {
438        Self { bytecode: Some(CompactBytecode::empty()), immutable_references: Default::default() }
439    }
440
441    pub fn bytes(&self) -> Option<&Bytes> {
443        self.bytecode.as_ref().and_then(|bytecode| bytecode.object.as_bytes())
444    }
445
446    pub fn into_bytes(self) -> Option<Bytes> {
448        self.bytecode.and_then(|bytecode| bytecode.object.into_bytes())
449    }
450
451    pub fn source_map(&self) -> Option<Result<SourceMap, SyntaxError>> {
455        self.bytecode.as_ref().and_then(|bytecode| bytecode.source_map())
456    }
457}
458
459impl From<DeployedBytecode> for CompactDeployedBytecode {
460    fn from(bcode: DeployedBytecode) -> Self {
461        Self {
462            bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()),
463            immutable_references: bcode.immutable_references,
464        }
465    }
466}
467
468impl From<CompactDeployedBytecode> for DeployedBytecode {
469    fn from(bcode: CompactDeployedBytecode) -> Self {
470        Self {
471            bytecode: bcode.bytecode.map(|d_bcode| d_bcode.into()),
472            immutable_references: bcode.immutable_references,
473        }
474    }
475}
476
477#[cfg(test)]
478mod tests {
479    use crate::{ConfigurableContractArtifact, ContractBytecode};
480
481    #[test]
482    fn test_empty_bytecode() {
483        let empty = r#"
484        {
485  "abi": [],
486  "bytecode": {
487    "object": "0x",
488    "linkReferences": {}
489  },
490  "deployedBytecode": {
491    "object": "0x",
492    "linkReferences": {}
493  }
494  }
495        "#;
496
497        let artifact: ConfigurableContractArtifact = serde_json::from_str(empty).unwrap();
498        let contract = artifact.into_contract_bytecode();
499        let bytecode: ContractBytecode = contract.into();
500        let bytecode = bytecode.unwrap();
501        assert!(!bytecode.bytecode.object.is_unlinked());
502    }
503}