Skip to content

Commit 5001e9b

Browse files
committed
Add create2 Yul primitive and test
1 parent a0003ec commit 5001e9b

File tree

6 files changed

+195
-1
lines changed

6 files changed

+195
-1
lines changed

integration/stylus/create2.sol

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.0;
3+
4+
interface ArbWasm {
5+
/// @notice Activate a wasm program
6+
/// @param program the program to activate
7+
/// @return version the stylus version the program was activated against
8+
/// @return dataFee the data fee paid to store the activated program
9+
function activateProgram(
10+
address program
11+
) external payable returns (uint16 version, uint256 dataFee);
12+
}
13+
14+
contract C {
15+
function test_create2() public payable {
16+
Greeter greeter = new Greeter();
17+
print("greeter = {}".format(address(greeter)));
18+
19+
ArbWasm arbWasm = ArbWasm(address(0x71));
20+
(uint16 version, uint256 dataFee) = arbWasm.activateProgram{
21+
value: msg.value
22+
}(address(greeter));
23+
print("version = {}".format(version));
24+
print("dataFee = {}".format(dataFee));
25+
26+
greeter.greet();
27+
28+
bytes initCode = contractDeploymentCalldata(address(greeter).code);
29+
print("initCode = {}".format(initCode));
30+
31+
Greeter greeter0;
32+
assembly {
33+
greeter0 := create2(
34+
0,
35+
initCode,
36+
0, // codeLen - ignored
37+
0 // salt
38+
)
39+
}
40+
print("greeter0 = {}".format(address(greeter0)));
41+
greeter0.greet();
42+
43+
Greeter greeter1;
44+
assembly {
45+
greeter1 := create2(
46+
0,
47+
initCode,
48+
0, // codeLen - ignored
49+
1 // salt
50+
)
51+
}
52+
print("greeter1 = {}".format(address(greeter1)));
53+
greeter1.greet();
54+
}
55+
}
56+
57+
contract Greeter {
58+
function greet() public view {
59+
print("Greetings from 0x{}!".format(this));
60+
}
61+
}
62+
63+
function contractDeploymentCalldata(bytes code) pure returns (bytes) {
64+
bytes memory deploy;
65+
uint256 codeLen = code.length;
66+
deploy.push(0x7f); // PUSH32
67+
deploy = pushBytes(deploy, bytes32(codeLen));
68+
deploy.push(0x80); // DUP1
69+
deploy.push(0x60); // PUSH1
70+
deploy.push(43); // prelude + version
71+
deploy.push(0x60); // PUSH1
72+
deploy.push(0x00);
73+
deploy.push(0x39); // CODECOPY
74+
deploy.push(0x60); // PUSH1
75+
deploy.push(0x00);
76+
deploy.push(0xf3); // RETURN
77+
deploy.push(0x00); // version
78+
deploy = pushBytes(deploy, code);
79+
return deploy;
80+
}
81+
82+
function pushBytes(bytes memory xs, bytes memory ys) pure returns (bytes) {
83+
uint256 n = ys.length;
84+
for (uint256 i = 0; i < n; i++) {
85+
xs.push(ys[i]);
86+
}
87+
return xs;
88+
}

src/codegen/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,7 @@ pub enum Builtin {
18081808
ChainId,
18091809
ContractCode,
18101810
ContractCodehash,
1811+
Create2,
18111812
Gasleft,
18121813
GasLimit,
18131814
Gasprice,

src/codegen/yul/builtin.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ pub(crate) fn process_builtin(
117117
| YulBuiltInFunction::ReturnDataCopy
118118
// Functions that manage contracts
119119
| YulBuiltInFunction::Create
120-
| YulBuiltInFunction::Create2
121120
| YulBuiltInFunction::Call
122121
| YulBuiltInFunction::CallCode
123122
| YulBuiltInFunction::DelegateCall
@@ -144,6 +143,14 @@ pub(crate) fn process_builtin(
144143
Expression::Poison
145144
}
146145

146+
YulBuiltInFunction::Create2 => {
147+
let value = expression(&args[0], contract_no, ns, vartab, cfg, opt);
148+
let code = expression(&args[1], contract_no, ns, vartab, cfg, opt);
149+
let code_len = expression(&args[2], contract_no, ns, vartab, cfg, opt);
150+
let salt = expression(&args[3], contract_no, ns, vartab, cfg, opt);
151+
Expression::Builtin { loc: *loc, tys: vec![Type::Address(false)], kind: Builtin::Create2, args: vec![value, code, code_len, salt] }
152+
}
153+
147154
YulBuiltInFunction::Origin => {
148155
Expression::Builtin { loc: *loc, tys: vec![Type::Address(false)], kind: Builtin::Origin, args: vec![] }
149156
}

src/emit/stylus/target.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,7 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
864864
}
865865

866866
/// builtin expressions
867+
#[warn(unused_variables)]
867868
fn builtin<'b>(
868869
&self,
869870
bin: &Binary<'b>,
@@ -1056,6 +1057,65 @@ impl<'a> TargetRuntime<'a> for StylusTarget {
10561057
.left()
10571058
.unwrap()
10581059
}
1060+
Expression::Builtin {
1061+
kind: Builtin::Create2,
1062+
args,
1063+
..
1064+
} => {
1065+
let [value, code, code_len, salt] = args.as_slice() else {
1066+
panic!()
1067+
};
1068+
1069+
let value = expression(self, bin, value, vartab, function);
1070+
let code = expression(self, bin, code, vartab, function);
1071+
// smoelius: `code_len` is ignored.
1072+
let _code_len = expression(self, bin, code_len, vartab, function);
1073+
let salt = expression(self, bin, salt, vartab, function);
1074+
1075+
let code_ptr = bin.vector_bytes(code);
1076+
1077+
let code_len = bin.vector_len(code);
1078+
1079+
let value_ptr = bin
1080+
.builder
1081+
.build_alloca(bin.value_type(), "value_ptr")
1082+
.unwrap();
1083+
bin.builder.build_store(value_ptr, value).unwrap();
1084+
1085+
let salt_buf = bin.builder.build_alloca(bin.value_type(), "salt").unwrap();
1086+
bin.builder.build_store(salt_buf, salt).unwrap();
1087+
1088+
let address = bin
1089+
.builder
1090+
.build_array_alloca(
1091+
bin.context.i8_type(),
1092+
i32_const!(bin.ns.address_length as u64),
1093+
"address",
1094+
)
1095+
.unwrap();
1096+
1097+
let revert_data_len = bin
1098+
.builder
1099+
.build_alloca(bin.llvm_type(&ast::Type::Uint(32)), "revert_data_len")
1100+
.unwrap();
1101+
1102+
call!(
1103+
"create2",
1104+
&[
1105+
code_ptr.into(),
1106+
code_len.into(),
1107+
value_ptr.into(),
1108+
salt_buf.into(),
1109+
address.into(),
1110+
revert_data_len.into()
1111+
],
1112+
"create2"
1113+
);
1114+
1115+
bin.builder
1116+
.build_load(bin.address_type(), address, "address")
1117+
.unwrap()
1118+
}
10591119
Expression::Builtin {
10601120
kind: Builtin::Gasleft,
10611121
..

tests/stylus_tests/create2.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//! This test expects you to have a devnode running:
2+
//! <https://docs.arbitrum.io/run-arbitrum-node/run-nitro-dev-node>
3+
//!
4+
//! It also expects `cargo-stylus` and `cast` to be installed:
5+
//! - <https://github.com/OffchainLabs/cargo-stylus>
6+
//! - <https://book.getfoundry.sh/cast/>
7+
#![warn(clippy::pedantic)]
8+
9+
use crate::{deploy, send, MUTEX};
10+
use std::{io::Write, path::PathBuf};
11+
12+
#[test]
13+
fn create2() {
14+
writeln!(
15+
std::io::stderr(),
16+
"If you run the `create2` test twice, it will fail the second time because the \
17+
contract `Greeter` cannot be activated twice.",
18+
)
19+
.unwrap();
20+
21+
let _lock = MUTEX.lock();
22+
let (tempdir, address) = deploy(
23+
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("integration/stylus/create2.sol"),
24+
"C",
25+
true,
26+
)
27+
.unwrap();
28+
let dir = &tempdir;
29+
30+
let stdout = send(
31+
dir,
32+
&address,
33+
["test_create2()", "--value=1000000000000000000"],
34+
)
35+
.unwrap();
36+
println!("{}", stdout);
37+
}

tests/stylus_tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod api;
22
mod arb_wasm;
33
mod counter;
4+
mod create2;
45
mod milestone_1;
56
mod milestone_2;
67
mod milestone_3;

0 commit comments

Comments
 (0)