foundry_common/preprocessor/
data.rs
1use super::span_to_range;
2use foundry_compilers::artifacts::{Source, Sources};
3use path_slash::PathExt;
4use solar_parse::interface::{Session, SourceMap};
5use solar_sema::{
6 hir::{Contract, ContractId, Hir},
7 interface::source_map::FileName,
8};
9use std::{
10 collections::{BTreeMap, HashSet},
11 path::{Path, PathBuf},
12};
13
14pub type PreprocessorData = BTreeMap<ContractId, ContractData>;
17
18pub(crate) fn collect_preprocessor_data(
20 sess: &Session,
21 hir: &Hir<'_>,
22 referenced_contracts: &HashSet<ContractId>,
23) -> PreprocessorData {
24 let mut data = PreprocessorData::default();
25 for contract_id in referenced_contracts {
26 let contract = hir.contract(*contract_id);
27 let source = hir.source(contract.source);
28
29 let FileName::Real(path) = &source.file.name else {
30 continue;
31 };
32
33 let contract_data =
34 ContractData::new(hir, *contract_id, contract, path, source, sess.source_map());
35 data.insert(*contract_id, contract_data);
36 }
37 data
38}
39
40pub(crate) fn create_deploy_helpers(data: &BTreeMap<ContractId, ContractData>) -> Sources {
44 let mut deploy_helpers = Sources::new();
45 for (contract_id, contract) in data {
46 if let Some(code) = contract.build_helper() {
47 let path = format!("foundry-pp/DeployHelper{}.sol", contract_id.get());
48 deploy_helpers.insert(path.into(), Source::new(code));
49 }
50 }
51 deploy_helpers
52}
53
54#[derive(Debug)]
56pub struct ContractConstructorData {
57 pub abi_encode_args: String,
59 pub struct_fields: String,
61}
62
63#[derive(Debug)]
65pub(crate) struct ContractData {
66 contract_id: ContractId,
68 path: PathBuf,
70 name: String,
72 pub constructor_data: Option<ContractConstructorData>,
74 pub artifact: String,
76}
77
78impl ContractData {
79 fn new(
80 hir: &Hir<'_>,
81 contract_id: ContractId,
82 contract: &Contract<'_>,
83 path: &Path,
84 source: &solar_sema::hir::Source<'_>,
85 source_map: &SourceMap,
86 ) -> Self {
87 let artifact = format!("{}:{}", path.to_slash_lossy(), contract.name);
88
89 let constructor_data = contract
91 .ctor
92 .map(|ctor_id| hir.function(ctor_id))
93 .filter(|ctor| !ctor.parameters.is_empty())
94 .map(|ctor| {
95 let mut abi_encode_args = vec![];
96 let mut struct_fields = vec![];
97 let mut arg_index = 0;
98 for param_id in ctor.parameters {
99 let src = source.file.src.as_str();
100 let loc = span_to_range(source_map, hir.variable(*param_id).span);
101 let mut new_src = src[loc].replace(" memory ", " ").replace(" calldata ", " ");
102 if let Some(ident) = hir.variable(*param_id).name {
103 abi_encode_args.push(format!("args.{}", ident.name));
104 } else {
105 arg_index += 1;
107 abi_encode_args.push(format!("args.foundry_pp_ctor_arg{arg_index}"));
108 new_src.push_str(&format!(" foundry_pp_ctor_arg{arg_index}"));
109 }
110 struct_fields.push(new_src);
111 }
112
113 ContractConstructorData {
114 abi_encode_args: abi_encode_args.join(", "),
115 struct_fields: struct_fields.join("; "),
116 }
117 });
118
119 Self {
120 contract_id,
121 path: path.to_path_buf(),
122 name: contract.name.to_string(),
123 constructor_data,
124 artifact,
125 }
126 }
127
128 pub fn build_helper(&self) -> Option<String> {
172 let Self { contract_id, path, name, constructor_data, artifact: _ } = self;
173
174 let Some(constructor_details) = constructor_data else { return None };
175 let contract_id = contract_id.get();
176 let struct_fields = &constructor_details.struct_fields;
177 let abi_encode_args = &constructor_details.abi_encode_args;
178
179 let helper = format!(
180 r#"
181pragma solidity >=0.4.0;
182
183import "{path}";
184
185abstract contract DeployHelper{contract_id} is {name} {{
186 struct ConstructorArgs {{
187 {struct_fields};
188 }}
189}}
190
191function encodeArgs{contract_id}(DeployHelper{contract_id}.ConstructorArgs memory args) pure returns (bytes memory) {{
192 return abi.encode({abi_encode_args});
193}}
194 "#,
195 path = path.to_slash_lossy(),
196 );
197
198 Some(helper)
199 }
200}