Skip to content

Commit b605610

Browse files
committed
Support value and salt in constructors
1 parent 0d0f5d9 commit b605610

File tree

4 files changed

+74
-37
lines changed

4 files changed

+74
-37
lines changed

Diff for: crates/compilers/src/preprocessor/data.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,12 @@ impl ContractData {
171171
/// vm.deployCode("artifact path", encodeArgs335(DeployHelper335.ConstructorArgs({name: name, symbol: symbol})))
172172
/// ```
173173
pub fn build_helper(&self) -> Option<String> {
174-
let Self { contract_id, path, name, constructor_data, artifact } = self;
174+
let Self { contract_id, path, name, constructor_data, artifact: _ } = self;
175175

176176
let Some(constructor_details) = constructor_data else { return None };
177177
let contract_id = contract_id.get();
178178
let struct_fields = &constructor_details.struct_fields;
179179
let abi_encode_args = &constructor_details.abi_encode_args;
180-
let vm_interface_name = format!("VmContractHelper{contract_id}");
181-
let vm = format!("{vm_interface_name}(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)");
182180

183181
let helper = format!(
184182
r#"
@@ -195,16 +193,6 @@ abstract contract DeployHelper{contract_id} is {name} {{
195193
function encodeArgs{contract_id}(DeployHelper{contract_id}.ConstructorArgs memory args) pure returns (bytes memory) {{
196194
return abi.encode({abi_encode_args});
197195
}}
198-
199-
function deployCode{contract_id}(DeployHelper{contract_id}.ConstructorArgs memory args) returns({name}) {{
200-
return {name}(payable({vm}.deployCode("{artifact}", encodeArgs{contract_id}(args))));
201-
}}
202-
203-
interface {vm_interface_name} {{
204-
function deployCode(string memory _artifact, bytes memory _data) external returns (address);
205-
function deployCode(string memory _artifact) external returns (address);
206-
function getCode(string memory _artifact) external returns (bytes memory);
207-
}}
208196
"#,
209197
path = path.to_slash_lossy(),
210198
);

Diff for: crates/compilers/src/preprocessor/deps.rs

+55-24
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
use itertools::Itertools;
99
use solar_parse::interface::Session;
1010
use solar_sema::{
11-
hir::{ContractId, Expr, ExprKind, Hir, TypeKind, Visit},
11+
hir::{ContractId, Expr, ExprKind, Hir, NamedArg, TypeKind, Visit},
1212
interface::{data_structures::Never, source_map::FileName, SourceMap},
1313
};
1414
use std::{
@@ -95,8 +95,9 @@ impl PreprocessorDependencies {
9595
enum BytecodeDependencyKind {
9696
/// `type(Contract).creationCode`
9797
CreationCode,
98-
/// `new Contract`. Holds the name of the contract and args length.
99-
New(String, usize, usize),
98+
/// `new Contract`. Holds the name of the contract, args length and offset, `msg.value` (if
99+
/// any) and salt (if any).
100+
New(String, usize, usize, Option<String>, Option<String>),
100101
}
101102

102103
/// Represents a single bytecode dependency.
@@ -193,6 +194,8 @@ impl<'hir> Visit<'hir> for BytecodeDependencyCollector<'hir> {
193194
name.to_string(),
194195
args_len.to_usize(),
195196
offset,
197+
named_arg(&self.src, named_args, "value", self.source_map),
198+
named_arg(&self.src, named_args, "salt", self.source_map),
196199
),
197200
loc: SourceMapLocation::from_span(self.source_map, ty.span),
198201
referenced_contract: contract_id,
@@ -222,6 +225,21 @@ impl<'hir> Visit<'hir> for BytecodeDependencyCollector<'hir> {
222225
}
223226
}
224227

228+
/// Helper function to extract value of a given named arg.
229+
fn named_arg(
230+
src: &str,
231+
named_args: &Option<&[NamedArg<'_>]>,
232+
arg: &str,
233+
source_map: &SourceMap,
234+
) -> Option<String> {
235+
named_args.unwrap_or_default().into_iter().find(|named_arg| named_arg.name.as_str() == arg).map(
236+
|named_arg| {
237+
let named_arg_loc = SourceMapLocation::from_span(source_map, named_arg.value.span);
238+
src[named_arg_loc.start..named_arg_loc.end].to_string()
239+
},
240+
)
241+
}
242+
225243
/// Goes over all test/script files and replaces bytecode dependencies with cheatcode
226244
/// invocations.
227245
pub(crate) fn remove_bytecode_dependencies(
@@ -260,39 +278,46 @@ pub(crate) fn remove_bytecode_dependencies(
260278
format!("{vm}.getCode(\"{artifact}\")"),
261279
));
262280
}
263-
BytecodeDependencyKind::New(name, args_length, offset) => {
264-
if constructor_data.is_none() {
265-
// if there's no constructor, we can just call deployCode with one
266-
// argument
267-
updates.insert((
268-
dep.loc.start,
269-
dep.loc.end + args_length,
270-
format!("{name}(payable({vm}.deployCode(\"{artifact}\")))"),
271-
));
272-
} else {
273-
// if there's a constructor, we use our helper
281+
BytecodeDependencyKind::New(name, args_length, offset, value, salt) => {
282+
let mut update = format!("{name}(payable({vm}.deployCode({{");
283+
update.push_str(&format!("_artifact: \"{artifact}\""));
284+
285+
if let Some(value) = value {
286+
update.push_str(", ");
287+
update.push_str(&format!("_value: {value}"));
288+
}
289+
290+
if let Some(salt) = salt {
291+
update.push_str(", ");
292+
update.push_str(&format!("_salt: {salt}"));
293+
}
294+
295+
if constructor_data.is_some() {
296+
// Insert our helper
274297
used_helpers.insert(dep.referenced_contract);
275-
updates.insert((
276-
dep.loc.start,
277-
dep.loc.end + offset,
278-
format!(
279-
"deployCode{id}(DeployHelper{id}.ConstructorArgs",
280-
id = dep.referenced_contract.get()
281-
),
298+
299+
update.push_str(", ");
300+
update.push_str(&format!(
301+
"_args: encodeArgs{id}(DeployHelper{id}.ConstructorArgs",
302+
id = dep.referenced_contract.get()
282303
));
304+
updates.insert((dep.loc.start, dep.loc.end + offset, update));
283305
updates.insert((
284306
dep.loc.end + args_length,
285307
dep.loc.end + args_length,
286-
")".to_string(),
308+
")})))".to_string(),
287309
));
310+
} else {
311+
update.push_str("})))");
312+
updates.insert((dep.loc.start, dep.loc.end + args_length, update));
288313
}
289314
}
290315
};
291316
}
292317
let helper_imports = used_helpers.into_iter().map(|id| {
293318
let id = id.get();
294319
format!(
295-
"import {{DeployHelper{id}, encodeArgs{id}, deployCode{id}}} from \"foundry-pp/DeployHelper{id}.sol\";",
320+
"import {{DeployHelper{id}, encodeArgs{id}}} from \"foundry-pp/DeployHelper{id}.sol\";",
296321
)
297322
}).join("\n");
298323
updates.insert((
@@ -303,8 +328,14 @@ pub(crate) fn remove_bytecode_dependencies(
303328
{helper_imports}
304329
305330
interface {vm_interface_name} {{
306-
function deployCode(string memory _artifact, bytes memory _data) external returns (address);
307331
function deployCode(string memory _artifact) external returns (address);
332+
function deployCode(string memory _artifact, bytes32 _salt) external returns (address);
333+
function deployCode(string memory _artifact, bytes memory _args) external returns (address);
334+
function deployCode(string memory _artifact, bytes memory _args, bytes32 _salt) external returns (address);
335+
function deployCode(string memory _artifact, uint256 _value) external returns (address);
336+
function deployCode(string memory _artifact, uint256 _value, bytes32 _salt) external returns (address);
337+
function deployCode(string memory _artifact, bytes memory _args, uint256 _value) external returns (address);
338+
function deployCode(string memory _artifact, bytes memory _args, uint256 _value, bytes32 _salt) external returns (address);
308339
function getCode(string memory _artifact) external returns (bytes memory);
309340
}}"#
310341
),

Diff for: test-data/preprocessor/src/CounterE.sol

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Contracts with payable constructor
2+
contract CounterE {
3+
constructor() payable {}
4+
}
5+
6+
contract CounterF {
7+
constructor(uint256 x) payable {}
8+
}
9+
10+
contract CounterG {
11+
constructor(address) payable {}
12+
}
13+

Diff for: test-data/preprocessor/test/CounterTest.sol

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Counter as CounterV1} from "src/v1/Counter.sol";
33
import "src/CounterB.sol";
44
import "src/CounterC.sol";
55
import "src/CounterD.sol";
6+
import "src/CounterE.sol";
67

78
contract CounterTest {
89
Counter public counter;
@@ -12,6 +13,10 @@ contract CounterTest {
1213
CounterV1 public counterv1;
1314
Counter public counter5 = new Counter{salt: bytes32("123")}();
1415
CounterB public counter6 = new CounterB {salt: bytes32("123")} (address(this), 44, true, address(this));
16+
CounterE public counter7 = new CounterE{ value: 111, salt: bytes32("123")}();
17+
CounterF public counter8 = new CounterF{value: 222, salt: bytes32("123")}(11);
18+
CounterG public counter9 = new CounterG { value: 333, salt: bytes32("123") } (address(this));
19+
CounterG public counter10 = new CounterG{ value: 333 }(address(this));
1520

1621
function setUp() public {
1722
counter = new Counter();

0 commit comments

Comments
 (0)