Skip to content

Commit

Permalink
chore: scripts for deploying implementation contracts (#423)
Browse files Browse the repository at this point in the history
  • Loading branch information
lumtis authored Nov 20, 2024
1 parent 6fcc885 commit 03ce9e0
Show file tree
Hide file tree
Showing 25 changed files with 1,387 additions and 0 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

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

contract DeployERC20CustodyImplementation is Script {
function run() external {
address expectedImplAddress;
bytes32 implSalt = keccak256("ERC20Custody");

vm.startBroadcast();

// Compute expected implementation address
expectedImplAddress = vm.computeCreate2Address(
implSalt,
hashInitCode(type(ERC20Custody).creationCode)
);

// Deploy the implementation contract using CREATE2
ERC20Custody erc20CustodyImpl = new ERC20Custody{salt: implSalt}();
require(address(erc20CustodyImpl) != address(0), "erc20CustodyImpl deployment failed");
require(expectedImplAddress == address(erc20CustodyImpl), "impl address doesn't match expected address");

vm.stopBroadcast();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/Script.sol";
import "contracts/evm/GatewayEVM.sol";

contract DeployGatewayEVM is Script {
function run() external {
address expectedImplAddress;
bytes32 implSalt = keccak256("GatewayEVM");

vm.startBroadcast();

// Compute expected implementation address
expectedImplAddress = vm.computeCreate2Address(
implSalt,
hashInitCode(type(GatewayEVM).creationCode)
);

// Deploy the implementation contract using CREATE2
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");

vm.stopBroadcast();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/Script.sol";
import "contracts/zevm/GatewayZEVM.sol";

contract DeployGatewayZEVM is Script {
function run() external {
address expectedImplAddress;
bytes32 implSalt = keccak256("GatewayZEVM");

vm.startBroadcast();

// Compute expected implementation address
expectedImplAddress = vm.computeCreate2Address(
implSalt,
hashInitCode(type(GatewayZEVM).creationCode)
);

// Deploy the implementation contract using CREATE2
GatewayZEVM gatewayImpl = new GatewayZEVM{salt: implSalt}();
require(address(gatewayImpl) != address(0), "gatewayImpl deployment failed");
require(expectedImplAddress == address(gatewayImpl), "impl address doesn't match expected address");

vm.stopBroadcast();
}
}
112 changes: 112 additions & 0 deletions v2/scripts/deploy/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,115 @@ forge script scripts/deploy/deterministic/DeployGatewayZEVM.s.sol \
--broadcast
```

## Deploying protocol contracts implementation for upgrades

Protocol contracts (Gateway and ERC20Custody) follow ERC1967 standard. The contracts can be upgraded by deploying a new implementation and upgrading to this new implementation.

The implementation contracts don't require environment variables or paramters to be deployed.

**GatewayEVM**

Deploy a wew implementation of the GatewayEVM:

```
forge script scripts/deploy/deterministic/DeployGatewayEVMImplementation.s.sol \
--private-key <PRIVATE_KEY> \
--rpc-url <RPC_URL> \
--verify \
--etherscan-api-key <ETHERSCAN_API_KEY> \
--chain-id <CHAIN_ID> \
--broadcast
```

**ERC20Custody**

Deploy a wew implementation of the ERC20Custody:

```
forge script scripts/deploy/deterministic/DeployERC20CustodyImplementation.s.sol \
--private-key <PRIVATE_KEY> \
--rpc-url <RPC_URL> \
--verify \
--etherscan-api-key <ETHERSCAN_API_KEY> \
--chain-id <CHAIN_ID> \
--broadcast
```

**GatewayZEVM**

Deploy a wew implementation of the GatewayZEVM:

```
forge script scripts/deploy/deterministic/DeployGatewayZEVMImplementation.s.sol \
--private-key <PRIVATE_KEY> \
--rpc-url <RPC_URL> \
--verify \
--verifier blockscout \
--verifier-url <VERIFIER_URL> \
--chain-id <CHAIN_ID> \
--broadcast
```

After the implementation is deployed, the contract can be upgraded by calling the following function from the admin:

```
upgradeToAndCall(0, <implementation_address>, "")
```

## Simulating a protocol contract upgrade

The scripts in `upgrade` allow to locally simulate the upgrade process with the protocol contract and verify the state is not corrupted.

First a forked localnet must be started in a separate terminal. The RPC will be the connected EVM chain for testing `GatewayEVM` or `ERC20Custody`, or ZetaChain for `GatewayZEVM`

```
anvil --fork-url <rpc>
```
Example:
```
anvil --fork-url https://ethereum-sepolia.rpc.subquery.network/public
anvil --fork-url https://zetachain-athens.g.allthatnode.com/archive/evm
```

The following environment variable must be set:
```
export PROXY_ADDRESS=<proxy address of the contract to test>
export ADMIN_ADDRESS=<address of the admin that upgrade the contract>
```

Then the script can be run.

`GatewayEVM`:
```
forge script scripts/upgrade/SimulateGatewayEVMUpgrade.s.sol --rpc-url http://localhost:8545
```
`ERC20Custody`:
```
forge script scripts/upgrade/SimulateERC20CustodyUpgrade.s.sol --rpc-url http://localhost:8545
```
`GatewayZEVM`:
```
forge script scripts/upgrade/SimulateGatewayZEVMUpgrade.s.sol --rpc-url http://localhost:8545
```

The scripts will log the different state variables of the contract. There is no automatic assertion, the values must be manually checked against the current values of the proxy contract.

These scripts must be maintained in the future to log all eventual new variables.

Example output:

```
Script ran successfully.
== Logs ==
Upgraded contract state:
custody address: 0xD80BE3710F08D280F51115e072e5d2a778946cd7
tss address: 0x8531a5aB847ff5B22D855633C25ED1DA3255247e
zetaConnector address: 0x0000000000000000000000000000000000000000
zetaToken address: 0x1432612E60cad487C857E7D38AFf57134916c902
```


## Deploying a ZRC20 reference contract

ZRC20 contract is upgradable by the protocol but doesn't follow the `ERC1967Proxy` standard.
Expand Down Expand Up @@ -148,8 +257,11 @@ The contract can be upgraded with the following documentation: https://github.co
## All deployment scripts

- `deterministic/DeployERC20Custody.s.sol`: deploy the ERC20 custody contract on a connected chain
- `deterministic/DeployERC20CustodyImplementation.s.sol`: deploy the ERC20 custody contract implementation on a connected chain for a contract upgrade
- `deterministic/DeployGatewayEVM.s.sol`: deploy the gateway contract on a connected chain
- `deterministic/DeployGatewayEVMImplementation.s.sol`: deploy the gateway contract implementation on a connected chain for a contract upgrade
- `deterministic/DeployGatewayZEVM.s.sol`: deploy the gateway contract on ZetaChain
- `deterministic/DeployGatewayZEVMImplementation.s.sol`: deploy the gateway contract implementation on ZetaChain for a contract upgrade
- `deterministic/TestERC20.s.sol`: deploy a ERC20 for test purpose
- `deterministic/ZetaConnectorNonNative.s.sol`: deploy the ZETA connector contract on a connected chain, currently not used
- `deterministic/UpgradeGatewayEVM.s.sol`: upgrade the GatewayEVM contract to a test contract implementation, used for test purposes only
Expand Down
39 changes: 39 additions & 0 deletions v2/scripts/upgrade/SimulateERC20CustodyUpgrade.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

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

contract UpgradeSimulation is Script {
function run() external {
// Forked environment
address proxyAddress = vm.envAddress("PROXY_ADDRESS");
address adminAddress = vm.envAddress("ADMIN_ADDRESS");

// Deploy the new implementation contract
bytes32 implSalt = keccak256("ERC20Custody");
address erc20CustodyImpl = address(new ERC20Custody{salt: implSalt}());

ERC20Custody proxy = ERC20Custody(proxyAddress);

// Get the current state
address gateway = proxy.gateway();
address tssAddress = proxy.tssAddress();
bool supportsLegacy = proxy.supportsLegacy();

// Simulate the upgrade
vm.prank(adminAddress);
proxy.upgradeToAndCall(erc20CustodyImpl, "");

// After upgrade, verify that the state is intact
require(gateway == proxy.gateway(), "gateway address mismatch");
require(tssAddress == proxy.tssAddress(), "tss address mismatch");
require(supportsLegacy == proxy.supportsLegacy(), "supportsLegacy mismatch");

console.log("Upgraded contract state:");
console.log("gateway address:", address(proxy.gateway()));
console.log("tss address:", proxy.tssAddress());
console.log("supportsLegacy address:", proxy.supportsLegacy());
}
}
42 changes: 42 additions & 0 deletions v2/scripts/upgrade/SimulateGatewayEVMUpgrade.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "contracts/evm/GatewayEVM.sol";

contract UpgradeSimulation is Script {
function run() external {
// Forked environment
address proxyAddress = vm.envAddress("PROXY_ADDRESS");
address adminAddress = vm.envAddress("ADMIN_ADDRESS");

// Deploy the new implementation contract
bytes32 implSalt = keccak256("GatewayEVM");
address gatewayImpl = address(new GatewayEVM{salt: implSalt}());

GatewayEVM proxy = GatewayEVM(proxyAddress);

// Get the current state
address custody = proxy.custody();
address tssAddress = proxy.tssAddress();
address zetaConnector = proxy.zetaConnector();
address zetaToken = proxy.zetaToken();

// Simulate the upgrade
vm.prank(adminAddress);
proxy.upgradeToAndCall(gatewayImpl, "");

// After upgrade, verify that the state is intact
require(custody == proxy.custody(), "custody address mismatch");
require(tssAddress == proxy.tssAddress(), "tss address mismatch");
require(zetaConnector == proxy.zetaConnector(), "zetaConnector address mismatch");
require(zetaToken == proxy.zetaToken(), "zetaToken address mismatch");

console.log("Upgraded contract state:");
console.log("custody address:", proxy.custody());
console.log("tss address:", proxy.tssAddress());
console.log("zetaConnector address:", proxy.zetaConnector());
console.log("zetaToken address:", proxy.zetaToken());
}
}
33 changes: 33 additions & 0 deletions v2/scripts/upgrade/SimulateGatewayZEVMUpgrade.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/console.sol";
import "forge-std/Script.sol";
import "contracts/zevm/GatewayZEVM.sol";

contract UpgradeSimulation is Script {
function run() external {
// Forked environment
address payable proxyAddress = payable(vm.envAddress("PROXY_ADDRESS"));
address adminAddress = vm.envAddress("ADMIN_ADDRESS");

// Deploy the new implementation contract
bytes32 implSalt = keccak256("GatewayZEVM");
address gatewayImpl = address(new GatewayZEVM{salt: implSalt}());

GatewayZEVM proxy = GatewayZEVM(proxyAddress);

// Get the current state
address zetaToken = proxy.zetaToken();

// Simulate the upgrade
vm.prank(adminAddress);
proxy.upgradeToAndCall(gatewayImpl, "");

// After upgrade, verify that the state is intact
require(zetaToken == proxy.zetaToken(), "zetaToken address mismatch");

console.log("Upgraded contract state:");
console.log("zetaToken address:", proxy.zetaToken());
}
}

0 comments on commit 03ce9e0

Please sign in to comment.