Skip to content

Commit

Permalink
feat: v2 evm contracts deploy scripts (#293)
Browse files Browse the repository at this point in the history
  • Loading branch information
skosito authored Aug 7, 2024
1 parent eff74bf commit 1b8e3c7
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ crytic-export

out
cache_forge
broadcast
v2/broadcast/*/31337
8 changes: 8 additions & 0 deletions v2/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This is sample on how .env should look like. Values given here are illustration and are expected if run with anvil locally and first private key.
# evm
TSS_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8
GATEWAY_ADMIN_ADDRESS_EVM=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
ERC20_CUSTODY_ADMIN_ADDRESS_EVM=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
ZETA_CONNECTOR_ADMIN_ADDRESS_EVM=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
GATEWAY_PROXY_EVM=0x9f21B726FCb84D8e92cdC678772590dce5347D0B
ZETA_ERC20_EVM=0x42928581Ba60cD97B65D873151dc063F3D0619f8
29 changes: 29 additions & 0 deletions v2/scripts/deploy/deterministic/DeployERC20Custody.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/Script.sol";
import "src/evm/ERC20Custody.sol";

contract DeployERC20Custody is Script {
function run() external {
address payable tss = payable(vm.envAddress("TSS_ADDRESS"));
address admin = vm.envAddress("ERC20_CUSTODY_ADMIN_ADDRESS_EVM");
address gateway = vm.envAddress("GATEWAY_PROXY_EVM");

bytes32 salt = keccak256("ERC20Custody");

vm.startBroadcast();

ERC20Custody custody = new ERC20Custody{salt: salt}(gateway, tss, admin);
require(address(custody) != address(0), "deployment failed");

address expectedAddr = vm.computeCreate2Address(
salt,
hashInitCode(type(ERC20Custody).creationCode, abi.encode(gateway, tss, admin))
);

require(expectedAddr == address(custody), "erc20 custody address doesn't match expected address");

vm.stopBroadcast();
}
}
28 changes: 12 additions & 16 deletions v2/scripts/deploy/deterministic/DeployGatewayEVM.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,28 @@ import "src/evm/GatewayEVM.sol";
import "test/utils/TestERC20.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract DeployGatewayEVMCreate2 is Script {
contract DeployGatewayEVM 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 payable tss = payable(vm.envAddress("TSS_ADDRESS"));
address admin = vm.envAddress("GATEWAY_ADMIN_ADDRESS_EVM");
address zeta = vm.envAddress("ZETA_ERC20_EVM");

address expectedImplAddress;
address expectedProxyAddress;

bytes32 implSalt = bytes32(uint256(10));
bytes32 proxySalt = bytes32(uint256(11));

vm.startBroadcast(deployer);
bytes32 implSalt = keccak256("GatewayEVM");
bytes32 proxySalt = keccak256("GatewayEVMProxy");

// TODO (https://github.com/zeta-chain/protocol-contracts/issues/251): should be passed as arg
TestERC20 zeta = new TestERC20("zeta", "ZETA");
vm.startBroadcast();

expectedImplAddress = computeCreate2Address(
expectedImplAddress = vm.computeCreate2Address(
implSalt,
hashInitCode(type(GatewayEVM).creationCode)
);

GatewayEVM gatewayImpl = new GatewayEVM{salt: implSalt}();
require(address(gatewayImpl) != address(0), "gatewayImpl deployment failed");

require(expectedImplAddress == address(gatewayImpl), "impl address doesn't match expected address");

expectedProxyAddress = vm.computeCreate2Address(
Expand All @@ -51,6 +45,8 @@ contract DeployGatewayEVMCreate2 is Script {
address(gatewayImpl),
abi.encodeWithSelector(GatewayEVM.initialize.selector, tss, address(zeta), admin)
);
require(address(gatewayProxy) != address(0), "gatewayProxy deployment failed");


require(expectedProxyAddress == address(gatewayProxy), "proxy address doesn't match expected address");

Expand Down
26 changes: 26 additions & 0 deletions v2/scripts/deploy/deterministic/DeployTestERC20.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/Script.sol";
import "test/utils/TestERC20.sol";

// This is just to deploy test erc20 tokens for testing deployments
contract DeployTestERC20 is Script {
function run() external {
bytes32 erc20Salt = keccak256("TestERC20");

vm.startBroadcast();

TestERC20 zeta = new TestERC20{salt: erc20Salt}("zeta", "ZETA");
require(address(zeta) != address(0), "deployment failed");

address expectedAddr = vm.computeCreate2Address(
erc20Salt,
hashInitCode(type(TestERC20).creationCode, abi.encode("zeta", "ZETA"))
);

require(expectedAddr == address(zeta), "zeta address doesn't match expected address");

vm.stopBroadcast();
}
}
30 changes: 30 additions & 0 deletions v2/scripts/deploy/deterministic/DeployZetaConnectorNonNative.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/Script.sol";
import "src/evm/ZetaConnectorNonNative.sol";

contract DeployZetaConnectorNonNative is Script {
function run() external {
address payable tss = payable(vm.envAddress("TSS_ADDRESS"));
address admin = vm.envAddress("ZETA_CONNECTOR_ADMIN_ADDRESS_EVM");
address gateway = vm.envAddress("GATEWAY_PROXY_EVM");
address zeta = vm.envAddress("ZETA_ERC20_EVM");

bytes32 salt = keccak256("ZetaConnectorNonNative");

vm.startBroadcast();

ZetaConnectorNonNative connector = new ZetaConnectorNonNative{salt: salt}(gateway, zeta, tss, admin);
require(address(connector) != address(0), "deployment failed");

address expectedAddr = vm.computeCreate2Address(
salt,
hashInitCode(type(ZetaConnectorNonNative).creationCode, abi.encode(gateway, zeta, tss, admin))
);

require(expectedAddr == address(connector), "zeta connector non native address doesn't match expected address");

vm.stopBroadcast();
}
}
8 changes: 1 addition & 7 deletions v2/scripts/deploy/deterministic/UpgradeGatewayEVM.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ 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));
address proxy = vm.envAddress("GATEWAY_EVM_PROXY");

GatewayEVM prevImpl = GatewayEVM(proxy);

Expand Down
13 changes: 11 additions & 2 deletions v2/scripts/deploy/deterministic/readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
## Deterministic GatewayEVM deployments
## Deterministic deployments

`DeployGatewayEVMCreate2` script uses create2 with Foundry (https://book.getfoundry.sh/tutorials/create2-tutorial) to perform deterministic deployment of `GatewayEVM` contracts.
Note: `.env` file should be set up and updated during deployments according to expected env variables in scripts, check `.env.sample` for example on how it should look like.
Currently, `.env.sample` is set with test env variables that can be used to test scripts locally with `anvil` using first account private key.

`DeployGatewayEVM` 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:
Expand All @@ -9,9 +12,15 @@ Since UUPS proxy is used for `GatewayEVM` contract, both implementation and `ERC
- adding a salt to deployment
- basic assertions to verify that deployed address is same as expected

Remaining deployment script work in similar way as `GatewayEVM` but much simpler because there is no proxy.

`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

To execute deployment script, following format is needed:

```
forge script scripts/deploy/deterministic/<Script>.s.sol --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --broadcast
```

0 comments on commit 1b8e3c7

Please sign in to comment.