Skip to content

Commit

Permalink
deployment working
Browse files Browse the repository at this point in the history
  • Loading branch information
dan13ram committed Sep 3, 2024
1 parent 493b12c commit 0f3a6fe
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 100 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
"main": "index.js",
"repository": "https://github.com/yieldnest/yieldnest-cross-chain",
"scripts": {
"build": "yarn install && forge build",
"build": "yarn install && forge install && forge build",
"compile": "forge compile",
"format": "forge fmt --root .",
"test": "forge test",
"test": "forge test -vvv",
"prepare": "husky"
}
}
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ forge-std/=node_modules/forge-std/src/
@layerzerolabs/lz-evm-protocol-v2/contracts/=node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/
@layerzerolabs/lz-evm-messagelib-v2/contracts/=node_modules/@layerzerolabs/lz-evm-messagelib-v2/contracts/
@layerzerolabs/lz-evm-oapp-v2/contracts-upgradeable/=node_modules/layerzero-v2/oapp/contracts/
@layerzerolabs/lz-evm-oapp-v2/test/=node_modules/layerzero-v2/oapp/test/
@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/
@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/
@solmate/=lib/solmate/src/
Expand Down
5 changes: 2 additions & 3 deletions src/L2YnERC20Upgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ contract L2YnERC20Upgradeable is ERC20Upgradeable, AccessControlUpgradeable, IMi
_disableInitializers();
}

function initialize(string memory _name, string memory _symbol, address _minter) public initializer {
function initialize(string memory _name, string memory _symbol, address _owner) public initializer {
__ERC20_init(_name, _symbol);
__AccessControl_init();

_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
_grantRole(MINTER_ROLE, _minter);
_grantRole(DEFAULT_ADMIN_ROLE, _owner);
}

function mint(address _to, uint256 _amount) public onlyRole(MINTER_ROLE) {
Expand Down
69 changes: 32 additions & 37 deletions src/factory/MultiChainDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ pragma solidity ^0.8.24;

import {CREATE3} from "solmate/utils/CREATE3.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol";
import {IMultiChainDeployer} from "@interfaces/IMultiChainDeployer.sol";
import {L2YnOFTAdapterUpgradeable} from "@adapters/L2YnOFTAdapterUpgradeable.sol";
import {L2YnERC20Upgradeable} from "@adapters/L2YnERC20Upgradeable.sol";
import "forge-std/console.sol";

contract MultiChainDeployer is IMultiChainDeployer {
event ContractCreated(address deployedAddress);
Expand All @@ -23,8 +23,6 @@ contract MultiChainDeployer is IMultiChainDeployer {
modifier containsCaller(bytes32 salt) {
// prevent contract submissions from being stolen from tx.pool by requiring
// that the first 20 bytes of the submitted salt match msg.sender.
console.log(address(bytes20(salt)));
console.log(msg.sender);
require(
(address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)),
"Invalid salt - first 20 bytes of the salt must match calling address."
Expand All @@ -33,18 +31,15 @@ contract MultiChainDeployer is IMultiChainDeployer {
}

/// @inheritdoc IMultiChainDeployer
function deploy(bytes32 salt, bytes calldata creationCode)
function deploy(bytes32 salt, bytes memory initCode)
public
payable
override
containsCaller(salt)
returns (address _deployedContract)
{
// move the initialization code from calldata to memory.
bytes memory initCode = creationCode;

// get target deployment
address targetDeploymentAddress = getDeployed(msg.sender, salt);
address targetDeploymentAddress = CREATE3.getDeployed(salt);

require(
!_deployed[targetDeploymentAddress],
Expand All @@ -65,32 +60,39 @@ contract MultiChainDeployer is IMultiChainDeployer {
}

/// @inheritdoc IMultiChainDeployer
function deployOFTAdapter(
bytes32 salt,
bytes calldata creationCode,
function deployL2YnOFTAdapter(
bytes32 _implSalt,
bytes32 _proxySalt,
address _token,
address _lzEndpoint,
address _owner,
RateLimiter.RateLimitConfig[] calldata _rateLimitConfigs
) public returns (address _deployedContract) {
//deploy the contract with create3
_deployedContract = this.deploy(salt, creationCode);
// initialize the YnOFTAdapter
initializeOFTAdapter(_deployedContract, _rateLimitConfigs);
bytes memory bytecode = type(L2YnOFTAdapterUpgradeable).creationCode;
bytes memory constructorParams = abi.encode(_token, _lzEndpoint);
bytes memory contractCode = abi.encodePacked(bytecode, constructorParams);

address adapterImpl = deploy(_implSalt, contractCode);
_deployedContract = deployProxy(_proxySalt, adapterImpl, _owner);
L2YnOFTAdapterUpgradeable(_deployedContract).initialize(_owner, _rateLimitConfigs);
}

/// @inheritdoc IMultiChainDeployer
function deployYnERC20(bytes32 salt, bytes calldata creationCode, string memory _name, string memory _symbol)
public
returns (address _deployedContract)
{
//deploy the contract with create3
_deployedContract = _deployedContract = this.deploy(salt, creationCode);
// initialize the deployed ERC20
initializeYnERC20Upgradeable(_deployedContract, _name, _symbol);
function deployL2YnERC20(
bytes32 _implSalt,
bytes32 _proxySalt,
string memory _name,
string memory _symbol,
address _owner
) public returns (address _deployedContract) {
address adapterImpl = deploy(_implSalt, type(L2YnERC20Upgradeable).creationCode);
_deployedContract = deployProxy(_proxySalt, adapterImpl, _owner);
L2YnERC20Upgradeable(_deployedContract).initialize(_name, _symbol, _owner);
}

/// @inheritdoc IMultiChainDeployer
function getDeployed(address deployer, bytes32 salt) public view override returns (address deployed) {
function getDeployed(bytes32 salt) public view override 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);
}

Expand All @@ -99,17 +101,10 @@ contract MultiChainDeployer is IMultiChainDeployer {
return _deployed[deploymentAddress];
}

/// @inheritdoc IMultiChainDeployer
function initializeOFTAdapter(address _deployedContract, RateLimiter.RateLimitConfig[] calldata _rateLimitConfigs)
public
{
L2YnOFTAdapterUpgradeable(_deployedContract).initialize(msg.sender, _rateLimitConfigs);
}

/// @inheritdoc IMultiChainDeployer
function initializeYnERC20Upgradeable(address _deployedContract, string memory _name, string memory _symbol)
public
{
L2YnERC20Upgradeable(_deployedContract).initialize(_name, _symbol, msg.sender);
function deployProxy(bytes32 salt, address implementation, address controller) internal returns (address proxy) {
bytes memory bytecode = type(TransparentUpgradeableProxy).creationCode;
bytes memory constructorParams = abi.encode(implementation, controller, "");
bytes memory contractCode = abi.encodePacked(bytecode, constructorParams);
proxy = deploy(salt, contractCode);
}
}
48 changes: 21 additions & 27 deletions src/interfaces/IMultiChainDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,46 @@ interface IMultiChainDeployer {

/// @notice Deploys a deployOFTAdapter contract using CREATE3 and initializes in the same call
/// @dev The provided salt is hashed together with msg.sender to generate the final salt
/// @param salt The deployer-specific salt for determining the deployed contract's address
/// @param creationCode The creation code of the contract to deploy
/// @param _implSalt the salt for the oft adapter to be passed to the initializer
/// @param _proxySalt the salt for the oft adapter to be passed to the initializer
/// @param _token the token address for the oft adapter to be passed to the initializer
/// @param _lzEndpoint the lz endpoint for the oft adapter to be passed to the initializer
/// @param _rateLimitConfigs the desired rate limit configs for the oft adapter to be passed to the initializer
/// @return deployed The address of the deployed contract
function deployOFTAdapter(
bytes32 salt,
bytes calldata creationCode,
function deployL2YnOFTAdapter(
bytes32 _implSalt,
bytes32 _proxySalt,
address _token,
address _lzEndpoint,
address _owner,
RateLimiter.RateLimitConfig[] calldata _rateLimitConfigs
) external returns (address deployed);

/// @notice Deploys a deployYnERC20 contract using CREATE3 and initializes in the same call
/// @dev The provided salt is hashed together with msg.sender to generate the final salt
/// @param salt The deployer-specific salt for determining the deployed contract's address
/// @param creationCode The creation code of the contract to deploy
/// @param _implSalt the salt for the oft adapter to be passed to the initializer
/// @param _proxySalt the salt for the oft adapter to be passed to the initializer
/// @param _name the name of the erc20 to be passed to the initializer
/// @param _symbol the symbol of the erc20 to be passed to the initializer
/// @param _owner the owner of the erc20 to be passed to the initializer
/// @return deployed The address of the deployed contract
function deployYnERC20(bytes32 salt, bytes calldata creationCode, string memory _name, string memory _symbol)
external
returns (address deployed);
function deployL2YnERC20(
bytes32 _implSalt,
bytes32 _proxySalt,
string memory _name,
string memory _symbol,
address _owner
) external returns (address deployed);

/// @notice Predicts the address of a deployed contract
/// @dev The provided salt is hashed together with the deployer address to generate the final salt
/// @param deployer The deployer account that will call deploy()
/// @param salt The deployer-specific salt for determining the deployed contract's address
/// @return deployed The address of the contract that will be deployed
function getDeployed(address deployer, bytes32 salt) external view returns (address deployed);
function getDeployed(bytes32 salt) external view returns (address deployed);

/// @dev Determine if a contract has already been deployed by the factory to a
/// given address.
/// @param deploymentAddress address The contract address to check.
/// @return True if the contract has been deployed, false otherwise.
function hasBeenDeployed(address deploymentAddress) external view returns (bool);

/// @notice Initializes a deployed YnOFTAdapter
/// @dev This is intended to be called atomically in the deployOFTAdapter, but can be called seperatly as well
/// @param _deployedContract the address of the deployed contract
/// @param _rateLimitConfigs the desired rate limit configs for the oft adapter
function initializeOFTAdapter(address _deployedContract, RateLimiter.RateLimitConfig[] calldata _rateLimitConfigs)
external;

/// @notice Initializes a deployed YnERC20Upgradeable contract
/// @dev This is intended to be called atomically in the deployYnERC20 call, but can be called seperatly as well
/// @param _deployedContract the address of the deployed contract
/// @param _name the name of the erc20
/// @param _symbol the symbol of the erc20
function initializeYnERC20Upgradeable(address _deployedContract, string memory _name, string memory _symbol)
external;
}
111 changes: 81 additions & 30 deletions test/CrossChainBaseTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,35 @@ import {L1YnOFTAdapterUpgradeable} from "@adapters/L1YnOFTAdapterUpgradeable.sol
import {L2YnERC20Upgradeable} from "@adapters/L2YnERC20Upgradeable.sol";
import {L2YnOFTAdapterUpgradeable} from "@adapters/L2YnOFTAdapterUpgradeable.sol";
import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol";
import {ERC20Mock} from "@layerzerolabs/lz-evm-oapp-v2/test/mocks/ERC20Mock.sol";
import "forge-std/console.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

contract CrossChainBaseTest is Test {
MultiChainDeployer public mainnetDeployer;
MultiChainDeployer public optimismDeployer;
MultiChainDeployer public baseDeployer;
MultiChainDeployer public arbitrumDeployer;
MultiChainDeployer public fraxDeployer;
// MultiChainDeployer public baseDeployer;
// MultiChainDeployer public fraxDeployer;

L1YnOFTAdapterUpgradeable public l1OFTAdapter;
L2YnERC20Upgradeable public l2YnERC20;
L2YnOFTAdapterUpgradeable public l2OFTAdapter;
L1YnOFTAdapterUpgradeable public mainnetOFTAdapter;
L2YnOFTAdapterUpgradeable public optimismOFTAdapter;
L2YnOFTAdapterUpgradeable public arbitrumOFTAdapter;

address public _deployer = address(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f);
address public _deployer = makeAddr("deployer");

address public arbitrumLzEndpoint = address(0x1a44076050125825900e736c501f859c50fE728c);
address public optimismLzEndpoint = address(0x1a44076050125825900e736c501f859c50fE728c);
address public mainnetLzEndpoint = address(0x1a44076050125825900e736c501f859c50fE728c);

ERC20Mock public mainnetERC20;
L2YnERC20Upgradeable public optimismERC20;
L2YnERC20Upgradeable public arbitrumERC20;

uint256 optimismFork;
uint256 arbitrumFork;
uint256 mainnetFork;

// uint256 holeskyFork;
// uint256 fraxFork;
// uint256 baseFork;
Expand All @@ -39,41 +50,81 @@ contract CrossChainBaseTest is Test {
// fraxFork = vm.createFork(vm.envString("FRAX_RPC_URL"));
// baseFork = vm.createFork(vm.envString("BASE_RPC_URL"));

vm.selectFork(mainnetFork);
mainnetDeployer = new MultiChainDeployer();
RateLimiter.RateLimitConfig[] memory _rateLimitConfigs = new RateLimiter.RateLimitConfig[](1);
_rateLimitConfigs[0] = RateLimiter.RateLimitConfig({dstEid: uint32(1), limit: 1 ether, window: 1 days});

vm.selectFork(optimismFork);
optimismDeployer = new MultiChainDeployer();
vm.startPrank(_deployer);

vm.selectFork(arbitrumFork);
arbitrumDeployer = new MultiChainDeployer();
{
vm.selectFork(mainnetFork);
mainnetDeployer = new MultiChainDeployer{salt: "SALT"}();
mainnetERC20 = new ERC20Mock("Test Token", "TEST");
address mainnetOFTAdapterImpl =
address(new L1YnOFTAdapterUpgradeable(address(mainnetERC20), mainnetLzEndpoint));
mainnetOFTAdapter = L1YnOFTAdapterUpgradeable(
address(new TransparentUpgradeableProxy(mainnetOFTAdapterImpl, _deployer, ""))
);
mainnetOFTAdapter.initialize(_deployer, _rateLimitConfigs);
}

{
vm.selectFork(optimismFork);
optimismDeployer = new MultiChainDeployer{salt: "SALT"}();
bytes32 optimismERC20Salt = createSalt(_deployer, "ERC20");
bytes32 optimismERC20ProxySalt = createSalt(_deployer, "ERC20Proxy");
optimismERC20 = L2YnERC20Upgradeable(
optimismDeployer.deployL2YnERC20(
optimismERC20Salt, optimismERC20ProxySalt, "Test Token", "TEST", _deployer
)
);
bytes32 optimismOFTAdapterSalt = createSalt(_deployer, "OFTAdapter");
bytes32 optimismOFTAdapterProxySalt = createSalt(_deployer, "OFTAdapterProxy");
optimismOFTAdapter = L2YnOFTAdapterUpgradeable(
optimismDeployer.deployL2YnOFTAdapter(
optimismOFTAdapterSalt,
optimismOFTAdapterProxySalt,
address(optimismERC20),
optimismLzEndpoint,
_deployer,
_rateLimitConfigs
)
);
}

{
vm.selectFork(arbitrumFork);
arbitrumDeployer = new MultiChainDeployer{salt: "SALT"}();
bytes32 arbitrumERC20Salt = createSalt(_deployer, "ERC20");
bytes32 arbitrumERC20ProxySalt = createSalt(_deployer, "ERC20Proxy");
arbitrumERC20 = L2YnERC20Upgradeable(
arbitrumDeployer.deployL2YnERC20(
arbitrumERC20Salt, arbitrumERC20ProxySalt, "Test Token", "TEST", _deployer
)
);
bytes32 arbitrumOFTAdapterSalt = createSalt(_deployer, "OFTAdapter");
bytes32 arbitrumOFTAdapterProxySalt = createSalt(_deployer, "OFTAdapterProxy");
arbitrumOFTAdapter = L2YnOFTAdapterUpgradeable(
arbitrumDeployer.deployL2YnOFTAdapter(
arbitrumOFTAdapterSalt,
arbitrumOFTAdapterProxySalt,
address(arbitrumERC20),
arbitrumLzEndpoint,
_deployer,
_rateLimitConfigs
)
);
}

// vm.selectFork(baseFork);
// baseDeployer = new MultiChainDeployer();

// vm.selectFork(fraxFork);
// fraxDeployer = new MultiChainDeployer();

bytes memory contractByteCode = type(L1YnOFTAdapterUpgradeable).creationCode;

// create salt where first 20 bytes matches the deployer address
bytes32 salt = createSalt(_deployer);

RateLimiter.RateLimitConfig[] memory _rateLimitConfigs = new RateLimiter.RateLimitConfig[](1);

RateLimiter.RateLimitConfig memory limitConfig =
RateLimiter.RateLimitConfig({dstEid: uint32(1), limit: 1 ether, window: 1 days});

_rateLimitConfigs[0] = limitConfig;

vm.selectFork(mainnetFork);
vm.startPrank(_deployer);
l1OFTAdapter =
L1YnOFTAdapterUpgradeable(mainnetDeployer.deployOFTAdapter(salt, contractByteCode, _rateLimitConfigs));
vm.stopPrank();
}

function createSalt(address deployerAddress) public pure returns (bytes32 _salt) {
_salt = bytes32(abi.encodePacked(bytes20(deployerAddress), bytes12("testing_test")));
function createSalt(address deployerAddress, string memory label) public pure returns (bytes32 _salt) {
_salt = bytes32(abi.encodePacked(bytes20(deployerAddress), bytes12(keccak256(bytes(label)))));
}
}
10 changes: 9 additions & 1 deletion test/MultiChainDeployer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,13 @@ import {L2YnOFTAdapterUpgradeable} from "@adapters/L2YnOFTAdapterUpgradeable.sol
import {RateLimiter} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol";

contract Test_MultiChainDeployer is CrossChainBaseTest {
function test_l1Deployment() public {}
function test_Deployment() public view {
assertEq(address(arbitrumDeployer), address(optimismDeployer));
assertNotEq(address(mainnetERC20), address(0));
assertNotEq(address(mainnetOFTAdapter), address(0));
assertNotEq(address(arbitrumERC20), address(0));
assertNotEq(address(arbitrumOFTAdapter), address(0));
assertEq(address(arbitrumERC20), address(optimismERC20));
assertEq(address(arbitrumOFTAdapter), address(optimismOFTAdapter));
}
}

0 comments on commit 0f3a6fe

Please sign in to comment.