From 8389089e689f8952e65d8ff315adc0c7625fc90e Mon Sep 17 00:00:00 2001 From: skosito Date: Tue, 6 Aug 2024 23:37:21 +0100 Subject: [PATCH] feat: create2 gatewayevm deployment and upgrade poc (#282) --- .../deterministic/DeployGatewayEVM.s.sol | 63 +++++++++++++++++++ .../deterministic/UpgradeGatewayEVM.s.sol | 34 ++++++++++ v2/scripts/deploy/deterministic/readme.md | 17 +++++ 3 files changed, 114 insertions(+) create mode 100644 v2/scripts/deploy/deterministic/DeployGatewayEVM.s.sol create mode 100644 v2/scripts/deploy/deterministic/UpgradeGatewayEVM.s.sol create mode 100644 v2/scripts/deploy/deterministic/readme.md diff --git a/v2/scripts/deploy/deterministic/DeployGatewayEVM.s.sol b/v2/scripts/deploy/deterministic/DeployGatewayEVM.s.sol new file mode 100644 index 00000000..224bff43 --- /dev/null +++ b/v2/scripts/deploy/deterministic/DeployGatewayEVM.s.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import "forge-std/Script.sol"; +import "src/evm/GatewayEVM.sol"; +import "test/utils/TestERC20.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract DeployGatewayEVMCreate2 is Script { + function run() external { + // TODO (https://github.com/zeta-chain/protocol-contracts/issues/251): should be passed as arg + string memory mnemonic = "test test test test test test test test test test test junk"; + uint256 privateKey = vm.deriveKey(mnemonic, 0); + address deployer = vm.rememberKey(privateKey); + + // TODO (https://github.com/zeta-chain/protocol-contracts/issues/251): should be passed as arg + address payable tss = payable(vm.envOr("TSS_ADDRESS", address(0x123))); + address admin = vm.envOr("ADMIN_ADDRESS", deployer); + + address expectedImplAddress; + address expectedProxyAddress; + + bytes32 implSalt = bytes32(uint256(10)); + bytes32 proxySalt = bytes32(uint256(11)); + + vm.startBroadcast(deployer); + + // TODO (https://github.com/zeta-chain/protocol-contracts/issues/251): should be passed as arg + TestERC20 zeta = new TestERC20("zeta", "ZETA"); + + expectedImplAddress = computeCreate2Address( + implSalt, + hashInitCode(type(GatewayEVM).creationCode) + ); + + GatewayEVM gatewayImpl = new GatewayEVM{salt: implSalt}(); + require(expectedImplAddress == address(gatewayImpl), "impl address doesn't match expected address"); + + expectedProxyAddress = vm.computeCreate2Address( + proxySalt, + hashInitCode( + type(ERC1967Proxy).creationCode, + abi.encode( + address(gatewayImpl), + abi.encodeWithSelector(GatewayEVM.initialize.selector, tss, address(zeta), admin) + ) + ) + ); + + ERC1967Proxy gatewayProxy = new ERC1967Proxy{salt: proxySalt}( + address(gatewayImpl), + abi.encodeWithSelector(GatewayEVM.initialize.selector, tss, address(zeta), admin) + ); + + require(expectedProxyAddress == address(gatewayProxy), "proxy address doesn't match expected address"); + + GatewayEVM gateway = GatewayEVM(address(gatewayProxy)); + require(gateway.tssAddress() == tss, "tss not set"); + require(gateway.zetaToken() == address(zeta), "zeta token not set"); + + vm.stopBroadcast(); + } +} \ No newline at end of file diff --git a/v2/scripts/deploy/deterministic/UpgradeGatewayEVM.s.sol b/v2/scripts/deploy/deterministic/UpgradeGatewayEVM.s.sol new file mode 100644 index 00000000..dfe6f383 --- /dev/null +++ b/v2/scripts/deploy/deterministic/UpgradeGatewayEVM.s.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import "forge-std/Script.sol"; +import "src/evm/GatewayEVM.sol"; +import "test/utils/GatewayEVMUpgradeTest.sol"; +import "test/utils/TestERC20.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; + +contract UpgradeGatewayEVM is Script { + function run() external { + // TODO (https://github.com/zeta-chain/protocol-contracts/issues/251): should be passed as arg + string memory mnemonic = "test test test test test test test test test test test junk"; + uint256 privateKey = vm.deriveKey(mnemonic, 0); + address deployer = vm.rememberKey(privateKey); + + // TODO (https://github.com/zeta-chain/protocol-contracts/issues/251): should be passed as arg + address proxy = vm.envOr("PROXY_ADDRESS", address(0xA7806c719bd377F15bA6CaDf2F94Afb7FfA66256)); + + GatewayEVM prevImpl = GatewayEVM(proxy); + + vm.startBroadcast(deployer); + + Upgrades.upgradeProxy(proxy, "GatewayEVMUpgradeTest.sol", ""); + + GatewayEVM newImpl = GatewayEVM(proxy); + + require(prevImpl.tssAddress() == newImpl.tssAddress(), "tss addr changed"); + require(prevImpl.zetaToken() == newImpl.zetaToken(), "zeta token addr changed"); + + vm.stopBroadcast(); + } +} \ No newline at end of file diff --git a/v2/scripts/deploy/deterministic/readme.md b/v2/scripts/deploy/deterministic/readme.md new file mode 100644 index 00000000..2b851270 --- /dev/null +++ b/v2/scripts/deploy/deterministic/readme.md @@ -0,0 +1,17 @@ +## Deterministic GatewayEVM deployments + +`DeployGatewayEVMCreate2` script uses create2 with Foundry (https://book.getfoundry.sh/tutorials/create2-tutorial) to perform deterministic deployment of `GatewayEVM` contracts. +This ensures that on every EVM chain `GatewayEVM` contract will be on same address. + +Since UUPS proxy is used for `GatewayEVM` contract, both implementation and `ERC1967Proxy` are deployed using above technique: + +- calculate expected address +- adding a salt to deployment +- basic assertions to verify that deployed address is same as expected + +`UpgradeGatewayEVM` script uses OpenZeppelin's Foundry Upgrades plugin (https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades), to upgrade `GatewayEVM`: + +- deploy new implementation (doesn't need to be deterministic since proxy address doesn't change) +- use plugin to upgrade proxy + +