Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Do not merge] example: create3 and create2 #179

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .forge-snapshots/Create2FactoryTest#test_deploy.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1730265
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
393380
1 change: 1 addition & 0 deletions .forge-snapshots/Create3FactoryTest#test_deploy.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1765131
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
405640
107 changes: 107 additions & 0 deletions test/Create2Factory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol";
import "forge-std/Test.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {Vault} from "../src/Vault.sol";
import {CLPoolManager} from "../src/pool-cl/CLPoolManager.sol";

/// @notice Through the use of solmate create3, deploy contracts with deterministic addresses
contract Create2Factory {
function deploy(bytes32 salt, bytes memory creationCode) public payable returns (address deployed) {
// hash salt with the deployer address to give each deployer its own namespace
salt = keccak256(abi.encodePacked(msg.sender, salt));

return Create2.deploy(msg.value, salt, creationCode);
}

function getDeployed(address deployer, bytes32 salt, bytes32 bytecodeHash) public view returns (address addr) {
// hash salt with the deployer address to give each deployer its own namespace
salt = keccak256(abi.encodePacked(deployer, salt));

return Create2.computeAddress(salt, bytecodeHash);
}

/// @notice execute a call on a deployed contract
/// @dev if contract uses Ownable(msg.sender) - it would mean the owner is Create2Factory, so a call is required
function execute(bytes32 salt, bytes32 bytecodeHash, bytes calldata data) external {
address target = getDeployed(msg.sender, salt, bytecodeHash);
(bool success,) = target.call(data);
require(success, "Create2Factory: failed execute call");
}
}

contract Create2FactoryTest is Test, GasSnapshot {
Create2Factory create2Factory;
Vault vault;
CLPoolManager clPoolManager;

function setUp() public {
create2Factory = new Create2Factory();
}

function test_deploy_NonDeterministic() public {
// deploy
vault = new Vault();
snapLastCall("Create2FactoryTest#test_deploy_NonDeterministic");
}

function test_Create2_Deploy() public {
// deploy
bytes memory creationCode = type(Vault).creationCode;
bytes32 salt = bytes32(uint256(0x1234));
address deployed = create2Factory.deploy(salt, creationCode);
snapLastCall("Create2FactoryTest#test_deploy");

vault = Vault(deployed);
assertEq(vault.owner(), address(create2Factory));
}

function test_Create2_GetDeployed() public {
// deploy
bytes memory creationCode = type(Vault).creationCode;
bytes32 salt = bytes32(uint256(0x1234));
address deployed = create2Factory.deploy(salt, creationCode);

// get deployed
address getDeployed = create2Factory.getDeployed(address(this), salt, keccak256(creationCode));

// assert
assertEq(deployed, getDeployed);
}

function test_Create2_Execute() public {
// pre-req: deploy vault
bytes memory creationCode = type(Vault).creationCode;
bytes32 salt = bytes32(uint256(0x1234));
address deployed = create2Factory.deploy(salt, creationCode);
vault = Vault(deployed);
assertEq(vault.owner(), address(create2Factory));

address alice = makeAddr("alice");
bytes32 bytecodeHash = keccak256(creationCode);
bytes memory data = abi.encodeWithSignature("transferOwnership(address)", alice);
create2Factory.execute(salt, bytecodeHash, data);
assertEq(vault.owner(), alice);
}

function test_Create2_Execute_FrontRun() public {
// pre-req: deploy vault
bytes memory creationCode = type(Vault).creationCode;
bytes32 salt = bytes32(uint256(0x1234));
address deployed = create2Factory.deploy(salt, creationCode);
vault = Vault(deployed);
assertEq(vault.owner(), address(create2Factory));

// assume someone front-runs the transferOwnership call
address alice = makeAddr("alice");
vm.startPrank(alice);
bytes32 bytecodeHash = keccak256(abi.encodePacked(creationCode));
bytes memory data = abi.encodeWithSignature("transferOwnership(address)", alice);
create2Factory.execute(salt, bytecodeHash, data);

// frontrun doesn't work, the owner is still create2Factory
assertEq(vault.owner(), address(create2Factory));
}
}
71 changes: 71 additions & 0 deletions test/Create3Factory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol";
import "forge-std/Test.sol";
import {CREATE3} from "solmate/src/utils/CREATE3.sol";
import {Vault} from "../src/Vault.sol";
import {CLPoolManager} from "../src/pool-cl/CLPoolManager.sol";

/// @notice Through the use of solmate create3, deploy contracts with deterministic addresses
contract Create3Factory {
/// @notice deploy a contract with a deterministic address based on `salt + msg.sender (deployer)`
/// @dev this means that two contract with different creationCode can be deployed on the same address on different chains
function deploy(bytes32 salt, bytes memory creationCode) external payable returns (address deployed) {
// hash salt with the deployer address to give each deployer its own namespace
salt = keccak256(abi.encodePacked(msg.sender, salt));
return CREATE3.deploy(salt, creationCode, msg.value);
}

/// @notice get the deployed address of a contract with a deterministic address based on `salt + deployer`
function getDeployed(address deployer, bytes32 salt) public view returns (address deployed) {
// hash salt with the deployer address to give each deployer its own namespace
salt = keccak256(abi.encodePacked(deployer, salt));
return CREATE3.getDeployed(salt);
}
}

contract Create3FactoryTest is Test, GasSnapshot {
Create3Factory create3Factory;
Vault vault;
CLPoolManager clPoolManager;

function setUp() public {
create3Factory = new Create3Factory();
}

function test_deploy_NonDeterministic() public {
// deploy
vault = new Vault();
snapLastCall("Create3FactoryTest#test_deploy_NonDeterministic");
}

/// @dev showcase a need to pass in owner address
function test_Create3_Deploy() public {
// deploy
bytes memory creationCode = type(Vault).creationCode;
bytes32 salt = bytes32(uint256(0x1234));
address deployed = create3Factory.deploy(salt, creationCode);
snapLastCall("Create3FactoryTest#test_deploy");

vault = Vault(deployed);
// note equal as owner is the proxy contract, not factory
assertNotEq(vault.owner(), address(create3Factory));
}

function test_Create3_Deploy_CLPoolManager() public {
// deploy vault
bytes memory vaultCreationCode = type(Vault).creationCode;
bytes32 vaultSalt = bytes32(uint256(0x1234));
address deployedVault = create3Factory.deploy(vaultSalt, vaultCreationCode);
vault = Vault(deployedVault);

// deploy CLPoolManager
bytes memory pmCreationCode = type(CLPoolManager).creationCode;
bytes memory pmConstructorArgs = abi.encode(deployedVault);
bytes memory pmCreationcodeWithArgs = abi.encodePacked(pmCreationCode, pmConstructorArgs);
bytes32 pmSalt = bytes32(uint256(0x12345));
address deployedCLPoolManager = create3Factory.deploy(pmSalt, pmCreationcodeWithArgs);
clPoolManager = CLPoolManager(deployedVault);
}
}
Loading