Skip to content

Commit

Permalink
Merge branch 'main' into fix/static-logs
Browse files Browse the repository at this point in the history
  • Loading branch information
Karrq authored Aug 29, 2024
2 parents 22daf40 + bf71946 commit 2e15cc6
Show file tree
Hide file tree
Showing 9 changed files with 223 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
55 changes: 55 additions & 0 deletions crates/forge/tests/it/zk/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use foundry_test_utils::{forgetest_async, util, ZkSyncNode};

forgetest_async!(forge_zk_can_deploy_erc20, |prj, cmd| {
util::initialize(prj.root());
prj.add_source("ERC20.sol", include_str!("../../../../../testdata/zk/ERC20.sol")).unwrap();

let node = ZkSyncNode::start();
let url = node.url();

let private_key =
ZkSyncNode::rich_wallets().next().map(|(_, pk, _)| pk).expect("No rich wallets available");

cmd.forge_fuse().args([
"create",
"--zk-startup",
"./src/ERC20.sol:MyToken",
"--rpc-url",
url.as_str(),
"--private-key",
private_key,
]);

let (stdout, _) = cmd.output_lossy();
assert!(stdout.contains("Deployer: "));
assert!(stdout.contains("Deployed to: "));
});

forgetest_async!(forge_zk_can_deploy_token_receiver, |prj, cmd| {
util::initialize(prj.root());
prj.add_source(
"TokenReceiver.sol",
include_str!("../../../../../testdata/zk/TokenReceiver.sol"),
)
.unwrap();

let node = ZkSyncNode::start();
let url = node.url();

let private_key =
ZkSyncNode::rich_wallets().next().map(|(_, pk, _)| pk).expect("No rich wallets available");

cmd.forge_fuse().args([
"create",
"--zk-startup",
"./src/TokenReceiver.sol:TokenReceiver",
"--rpc-url",
url.as_str(),
"--private-key",
private_key,
]);

let (stdout, _) = cmd.output_lossy();
assert!(stdout.contains("Deployer: "));
assert!(stdout.contains("Deployed to: "));
});
1 change: 1 addition & 0 deletions crates/forge/tests/it/zk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod basic;
mod cheats;
mod contracts;
mod create;
mod factory;
mod factory_deps;
mod fork;
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;
}
}
29 changes: 29 additions & 0 deletions testdata/zk/ERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

contract MyToken {
string public owner;
string public constant name = "MyToken";
string public constant symbol = "MTK";
uint8 public constant decimals = 18;
uint256 public totalSupply = 1000000 * (10 ** uint256(decimals));
mapping(address => uint256) public balanceOf;

event Transfer(address indexed from, address indexed to, uint256 value);

constructor() {
balanceOf[msg.sender] = totalSupply;
}

function setTotalSupply(uint256 amount) public {
totalSupply = amount;
}

function transfer(address to, uint256 amount) public returns (bool) {
require(balanceOf[msg.sender] >= amount, "Not enough tokens");
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
}
12 changes: 12 additions & 0 deletions testdata/zk/TokenReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface IMyToken {
function transfer(address to, uint256 amount) external returns (bool);
}

contract TokenReceiver {
function receiveAndHoldToken(address token, uint256 amount) external {
IMyToken(token).transfer(msg.sender, amount);
}
}

0 comments on commit 2e15cc6

Please sign in to comment.