Skip to content

Commit

Permalink
test(zk): add test for create2 computation with L2Contract (#549)
Browse files Browse the repository at this point in the history
* test(zk): add create2 address check

refactor(test:zk): extract `computeCreate2Address`

* refactor(test:zk): move Create2Test to file

* chore: fmt

* chore: typo

* fix(test:zk): specify evm-version

chore: use consistent variable name
  • Loading branch information
Karrq authored Aug 29, 2024
1 parent 11424d0 commit bf71946
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 51 deletions.
64 changes: 64 additions & 0 deletions crates/forge/tests/fixtures/zk/Create2.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "forge-std/Test.sol";
import {L2ContractHelper} from "era-contracts/l2-contracts/contracts/L2ContractHelper.sol"; // =0.8.20

import {Greeter} from "../src/Greeter.sol";
import {CustomNumber} from "../src/CustomNumber.sol";

import {Create2Utils} from "../src/Create2Utils.sol";

contract Create2Test is Test {
function getBytecodeHash(string memory path) internal returns (bytes32 bytecodeHash) {
string memory artifact = vm.readFile(path);
bytecodeHash = vm.parseJsonBytes32(artifact, ".hash");
}

function testCanDeployViaCreate2() public {
bytes32 bytecodeHash = getBytecodeHash("zkout/Greeter.sol/Greeter.json");
address sender = address(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496);
bytes32 salt = "12345";
bytes32 constructorInputHash = keccak256(abi.encode());

address computedAddress =
Create2Utils.computeCreate2Address(sender, salt, bytes32(bytecodeHash), constructorInputHash);

// deploy via create2
address actualAddress = address(new Greeter{salt: salt}());

assertEq(actualAddress, computedAddress);
}

function testComputeCreate2WithNoArgs() external {
bytes32 salt = bytes32(0x0);

bytes32 bytecodeHash = getBytecodeHash("zkout/Greeter.sol/Greeter.json");

address computedAddress =
Create2Utils.computeCreate2Address(address(this), salt, bytes32(bytecodeHash), keccak256(abi.encode()));
address expectedAddress =
L2ContractHelper.computeCreate2Address(address(this), salt, bytes32(bytecodeHash), keccak256(abi.encode()));

address actualAddress = address(new Greeter{salt: salt}());
assertEq(actualAddress, expectedAddress);
assertEq(computedAddress, expectedAddress);
}

function testComputeCreate2WithArgs() external {
bytes32 salt = bytes32(0x0);
uint8 value = 42;

bytes32 bytecodeHash = getBytecodeHash("zkout/CustomNumber.sol/CustomNumber.json");

address computedAddress =
Create2Utils.computeCreate2Address(address(this), salt, bytecodeHash, keccak256(abi.encode(value)));
address expectedAddress =
L2ContractHelper.computeCreate2Address(address(this), salt, bytecodeHash, keccak256(abi.encode(value)));

CustomNumber num = new CustomNumber{salt: salt}(value);
assertEq(address(num), expectedAddress);
assertEq(computedAddress, expectedAddress);
assertEq(num.number(), value);
}
}
33 changes: 27 additions & 6 deletions crates/forge/tests/it/zk/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{config::*, test_helpers::TEST_DATA_DEFAULT};
use forge::revm::primitives::SpecId;
use foundry_config::fs_permissions::PathPermission;
use foundry_test_utils::Filter;
use foundry_test_utils::{util, Filter};

#[tokio::test(flavor = "multi_thread")]
async fn test_zk_contract_can_call_function() {
Expand Down Expand Up @@ -52,12 +52,33 @@ async fn test_zk_contract_deployment_balance_transfer() {

#[tokio::test(flavor = "multi_thread")]
async fn test_zk_contract_create2() {
let mut zk_config = TEST_DATA_DEFAULT.zk_test_data.zk_config.clone();
zk_config.fs_permissions.add(PathPermission::read_write("./zk/zkout/ConstantNumber.sol"));
let runner = TEST_DATA_DEFAULT.runner_with_zksync_config(zk_config);
let filter = Filter::new("testZkContractsCreate2", "ZkContractsTest", ".*");
let (prj, mut cmd) = util::setup_forge(
"test_zk_contract_create2_with_deps",
foundry_test_utils::foundry_compilers::PathStyle::Dapptools,
);
util::initialize(prj.root());

TestConfig::with_filter(runner, filter).evm_spec(SpecId::SHANGHAI).run().await;
cmd.args(["install", "matter-labs/era-contracts", "--no-commit", "--shallow"])
.ensure_execute_success()
.expect("able to install dependencies");
cmd.forge_fuse();

let mut config = cmd.config();
config.fs_permissions.add(PathPermission::read("./zkout"));
prj.write_config(config);

prj.add_source("Greeter.sol", include_str!("../../../../../testdata/zk/Greeter.sol")).unwrap();

prj.add_source("CustomNumber.sol", include_str!("../../../../../testdata/zk/CustomNumber.sol"))
.unwrap();

prj.add_source("Create2Utils.sol", include_str!("../../../../../testdata/zk/Create2Utils.sol"))
.unwrap();

prj.add_test("Create2.t.sol", include_str!("../../fixtures/zk/Create2.t.sol")).unwrap();

cmd.args(["test", "--zk-startup", "--evm-version", "shanghai", "--mc", "Create2Test"]);
assert!(cmd.stdout_lossy().contains("Suite result: ok"));
}

#[tokio::test(flavor = "multi_thread")]
Expand Down
46 changes: 1 addition & 45 deletions testdata/zk/Contracts.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "../cheats/Vm.sol";

import {ConstantNumber} from "./ConstantNumber.sol";
import {Greeter} from "./Greeter.sol";
import {CustomNumber} from "./CustomNumber.sol";
import {Globals} from "./Globals.sol";

interface ISystemContractDeployer {
Expand Down Expand Up @@ -65,18 +66,6 @@ contract PayableFixedNumber {
}
}

contract CustomNumber {
uint8 value;

constructor(uint8 _value) {
value = _value;
}

function number() public view returns (uint8) {
return value;
}
}

contract CustomStorage {
uint8 num;
string str;
Expand Down Expand Up @@ -209,23 +198,6 @@ contract ZkContractsTest is DSTest {
require(customStorage.getNum() == 10, "era inline contract value mismatch (complex args)");
}

function testZkContractsCreate2() public {
vm.selectFork(forkEra);

string memory artifact = vm.readFile("zk/zkout/ConstantNumber.sol/ConstantNumber.json");
bytes32 bytecodeHash = vm.parseJsonBytes32(artifact, ".hash");
address sender = address(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496);
bytes32 salt = "12345";
bytes32 constructorInputHash = keccak256(abi.encode());
address expectedDeployedAddress =
_computeCreate2Address(sender, salt, bytes32(bytecodeHash), constructorInputHash);

// deploy via create2
address actualDeployedAddress = address(new ConstantNumber{salt: salt}());

assertEq(expectedDeployedAddress, actualDeployedAddress);
}

function testZkContractsCallSystemContract() public {
(bool success,) = address(vm).call(abi.encodeWithSignature("zkVm(bool)", true));
require(success, "zkVm() call failed");
Expand Down Expand Up @@ -274,20 +246,4 @@ contract ZkContractsTest is DSTest {
assert(vm.getNonce(sender) == startingNonce + 3);
vm.stopBroadcast();
}

function _computeCreate2Address(
address sender,
bytes32 salt,
bytes32 creationCodeHash,
bytes32 constructorInputHash
) private pure returns (address) {
bytes32 zksync_create2_prefix = keccak256("zksyncCreate2");
bytes32 address_hash = keccak256(
bytes.concat(
zksync_create2_prefix, bytes32(uint256(uint160(sender))), salt, creationCodeHash, constructorInputHash
)
);

return address(uint160(uint256(address_hash)));
}
}
20 changes: 20 additions & 0 deletions testdata/zk/Create2Utils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.8.7 <0.9.0;

library Create2Utils {
function computeCreate2Address(address sender, bytes32 salt, bytes32 creationCodeHash, bytes32 constructorInputHash)
internal
pure
returns (address)
{
bytes32 zksync_create2_prefix = keccak256("zksyncCreate2");
bytes32 address_hash = keccak256(
bytes.concat(
zksync_create2_prefix, bytes32(uint256(uint160(sender))), salt, creationCodeHash, constructorInputHash
)
);

return address(uint160(uint256(address_hash)));
}
}
14 changes: 14 additions & 0 deletions testdata/zk/CustomNumber.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.7 <0.9.0;

contract CustomNumber {
uint8 value;

constructor(uint8 _value) {
value = _value;
}

function number() public view returns (uint8) {
return value;
}
}

0 comments on commit bf71946

Please sign in to comment.