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

Migration helper script #20

Merged
merged 2 commits into from
Aug 12, 2024
Merged
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
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
PRIVATE_KEY=
PROXY_ADDRESS=0x000000033763b9d6d94efd3209dc255686aa8fba
#PROXY_ADDRESS=0x000000007f56768de3133034fa730a909003a165
SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com
SHIBUYA_RPC_URL=https://evm.shibuya.astar.network
POLYGON_AMOY_RPC_URL=https://rpc-amoy.polygon.technology
93 changes: 93 additions & 0 deletions run-migration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/bin/bash
set -e

# By default, doesn't broadcast any transactions.
DRY_RUN=1

# Setup console colors
if test -t 1 && command -v tput >/dev/null 2>&1; then
ncolors=$(tput colors)
if test -n "${ncolors}" && test "${ncolors}" -ge 8; then
bold_color=$(tput bold)
green_color=$(tput setaf 2)
warn_color=$(tput setaf 3)
error_color=$(tput setaf 1)
reset_color=$(tput sgr0)
fi
# 72 used instead of 80 since that's the default of pr
ncols=$(tput cols)
fi
: "${ncols:=72}"

# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
--migrate)
unset DRY_RUN
shift 1
;;
--proxy=*)
PROXY_ADDRESS="${i#*=}"
shift 1
;;
-pk=*|--private-key=*)
PRIVATE_KEY="${i#*=}"
shift 1
;;
--sepolia-rpc=*)
SEPOLIA_RPC_URL="${i#*=}"
shift 1
;;
--shibuya-rpc=*)
SHIBUYA_RPC_URL="${i#*=}"
shift 1
;;
--amoy-rpc=*)
POLYGON_AMOY_RPC_URL="${i#*=}"
shift 1
;;
*)
warn "Unknown argument: $1"
echo "Usage: $0 --pk=<PRIVATE_KEY> --proxy=PROXY_ADDRESS [--migrate] [--sepolia-rpc=] [--shibuya-rpc=] [--amoy-rpc=]"
;;
esac
done

# Load .env file
if [ -f .env ]; then
echo "Load .env file"
source .env
else
echo ".env file not found, run 'cp .env.example .env' and fill the values"
fi

# Check if PRIVATE_KEY is set
if [ -z "${PRIVATE_KEY}" ]; then
echo "PRIVATE_KEY is not set"
exit 1
fi

# Check if PROXY_ADDRESS is set
if [ -z "${PROXY_ADDRESS}" ]; then
echo "PROXY_ADDRESS is not set"
exit 1
fi

# Set fork-url
PARAMS=(-vvvv)

# Verify if the migration is going to be broadcasted
if [ -z "${DRY_RUN}" ]; then
read -r -p "running in broadcast mode, the transaction will be broadcasted, are you sure you want to continue? [y/n] " response
case "$response" in
[yY][eE][sS]|[yY])
PARAMS+=(--broadcast)
;;
*)
echo "running in dry-mode..."
;;
esac
fi

forge script ./scripts/Migrate.sol "${PARAMS[@]}"
217 changes: 217 additions & 0 deletions scripts/Migrate.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// SPDX-License-Identifier: MIT
// Analog's Contracts (last updated v0.1.0) (scripts/Upgrade.sol)

pragma solidity ^0.8.0;

import {Script} from "forge-std/Script.sol";
import {console} from "forge-std/console.sol";
import {IGateway} from "../src/interfaces/IGateway.sol";
import {ERC1967} from "../src/utils/ERC1967.sol";
import {UFloat9x56, UFloatMath} from "../src/utils/Float9x56.sol";
import {Gateway} from "../src/Gateway.sol";
import {
TssKey,
GmpMessage,
UpdateKeysMessage,
UpdateNetworkInfo,
Signature,
Network,
GmpStatus,
GmpSender,
PrimitiveUtils
} from "../src/Primitives.sol";

contract MigrateGateway is Script {
bytes32 internal constant PROXY_CODEHASH = 0x54afeb06256bce71659256132ac18f1515de3011aaec4fbd6fc7b0c00c7263d8;

/**
* Information about the current state of the migration
* @param forkId The network fork id, see: https://book.getfoundry.sh/forge/fork-testing#forking-cheatcodes
* @param mortality The maximum block number where the migration can be executed.
* @param proxyAddress The address of the proxy contract
*/
struct State {
uint256 forkId;
uint64 mortality;
address proxyAddress;
}

/**
* @dev Maps the network id to its migration state
*/
mapping(uint16 => State) public states;

// Computes the EIP-712 domain separador
function _computeDomainSeparator(uint256 networkId, address addr) private pure returns (bytes32) {
return keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256("Analog Gateway Contract"),
keccak256("0.1.0"),
uint256(networkId),
address(addr)
)
);
}

/**
* @dev Retrieve network info and verify if the deployer is the admin of the proxy contract.
*/
function _setupNetwork(string memory name, address proxyAddress, address deployer)
private
returns (UpdateNetworkInfo memory info)
{
console.log(string(bytes.concat(" -- CHECKING ", bytes(name))));

// Retrieve the RPC URL
string memory rpcUrl = vm.envOr(name, string(""));
require(bytes(rpcUrl).length > 0, "rpc url not found");
console.log(" RPC", rpcUrl);

// Create a new fork
uint256 forkId = vm.createSelectFork(rpcUrl);

// Verify if the provided proxy address is valid
{
require(proxyAddress.code.length > 0, "UpgradeGateway: proxy doesn't exists");
bytes32 codehash;
assembly {
codehash := extcodehash(proxyAddress)
}
require(codehash == PROXY_CODEHASH, "UpgradeGateway: invalid proxy codehash");
}

// Allocate the network information
info = UpdateNetworkInfo({
networkId: 0,
domainSeparator: bytes32(0),
gasLimit: 0,
relativeGasPrice: UFloatMath.ONE,
baseFee: 0,
mortality: 0
});

// Retrieve the network id
info.networkId = IGateway(proxyAddress).networkId();
console.log(" NETWORK ID", info.networkId);

// Retrieve the proxy admin
address admin = address(uint160(uint256(vm.load(proxyAddress, ERC1967.ADMIN_SLOT))));
console.log(" PROXY_ADMIN", admin);

// Retrieve the current implementation
address implementation = address(uint160(uint256(vm.load(proxyAddress, ERC1967.IMPLEMENTATION_SLOT))));
console.log(" IMPLEMENATTION", implementation);

// Print information about the current network
console.log(" GATEWAY BALANCE", proxyAddress.balance);
console.log(" DEPLOYER BALANCE", deployer.balance);
console.log(" LATEST BLOCK", block.number);
console.log(" BLOCK GAS LIMIT", block.gaslimit);
console.log(" CHAIN ID", block.chainid);
console.log(" GAS PRICE", tx.gasprice);
console.log(" BASE FEE", block.basefee, "\n");

require(admin == deployer, "deployer is not the admin if this contract");
require(block.gaslimit < uint64(type(int64).max), "block gas limit exceeds the limit of int64");
require(block.gaslimit > 1_000_000, "block gas limit is too low");
require(block.number < uint64(type(int64).max), "block number limit exceeds the limit of int64");
require(block.number > 1_000_000, "block number is low, is this a local testnet?");

// Update network information
info.domainSeparator = _computeDomainSeparator(info.networkId, proxyAddress);
info.gasLimit = uint64(block.gaslimit >> 1);
info.relativeGasPrice = UFloatMath.ONE;
info.baseFee = 0;
info.mortality = uint64(block.number + 128);

// Save migration state information
states[info.networkId] = State({forkId: forkId, mortality: info.mortality, proxyAddress: proxyAddress});
}

/**
* @dev Verify the networks and check if the deployer is the admin of the proxy contract
*/
function _setupNetworks(address proxyAddress, address deployer)
private
returns (UpdateNetworkInfo[] memory networks)
{
networks = new UpdateNetworkInfo[](3);
networks[0] = _setupNetwork("SEPOLIA_RPC_URL", proxyAddress, deployer);

networks[1] = _setupNetwork("SHIBUYA_RPC_URL", proxyAddress, deployer);
require(networks[0].networkId != networks[1].networkId, "SEPOLIA and SHIBUYA have the same network id");

networks[2] = _setupNetwork("POLYGON_AMOY_RPC_URL", proxyAddress, deployer);
require(networks[2].networkId != networks[0].networkId, "AMOY and SEPOLIA have the same network id");
require(networks[2].networkId != networks[0].networkId, "AMOY and SHIBUYA have the same network id");
}

/**
* @dev Deploy the new Gateway implementation and upgrade the proxy contract
*/
function _upgradeNetwork(uint16 networkId, uint256 deployerPrivateKey, UpdateNetworkInfo[] memory networks)
private
{
State memory state = states[networkId];

// Switch to the fork
vm.selectFork(state.forkId);

// Check if the implementation expected address
{
// Retrieve the deployer nonce
address deployer = vm.addr(deployerPrivateKey);
uint256 nonce = vm.getNonce(deployer);
address implementation = vm.computeCreateAddress(deployer, nonce);
console.log(" NEW IMPLEMENATTION", implementation);
}

// Update message mortality
for (uint256 i = 0; i < networks.length; i++) {
networks[i].mortality = state.mortality;
}

// Deploy the new implementation contract
vm.startBroadcast(deployerPrivateKey);
Gateway newImplementation = new Gateway(networkId, state.proxyAddress);
console.log(" DEPLOYED", address(newImplementation));

bytes memory initializer = abi.encodeCall(Gateway.updateNetworks, (networks));
console.log(" INITIALIZER:");
console.logBytes(initializer);
Gateway(state.proxyAddress).upgradeAndCall(address(newImplementation), initializer);
console.log(" GATEWAY UPGRADED");
vm.stopBroadcast();
}

/**
* @dev Script entry point, the following core will upgrade the gateway contract of all networks.
*/
function run() external {
// Retrieve the gateway proxy address
address proxyAddress = vm.envAddress("PROXY_ADDRESS");
console.log(" PROXY_ADDRESS", proxyAddress);

// Retrieve deployer private key
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log(" DEPLOYER", deployer, "\n");

// Setup the networks
UpdateNetworkInfo[] memory networkInfos = _setupNetworks(proxyAddress, deployer);

// Extract the network ids
uint16[] memory networkByID = new uint16[](networkInfos.length);
for (uint256 i = 0; i < networkInfos.length; i++) {
networkByID[i] = networkInfos[i].networkId;
}

// Upgrade the networks
for (uint256 i = 0; i < networkByID.length; i++) {
uint16 networkID = networkByID[i];
console.log(" -- UPGRADING NETWORK", networkID);
_upgradeNetwork(networkID, deployerPrivateKey, networkInfos);
}
}
}
13 changes: 13 additions & 0 deletions src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,19 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 {
_setNetworkInfo(bytes32(uint256(uint160(_getAdmin()))), messageHash, info);
}

/**
* @dev set network info using admin account
*/
function updateNetworks(UpdateNetworkInfo[] calldata networks) external {
require(networks.length > 0, "networks cannot be empty");
require(msg.sender == _getAdmin(), "unauthorized");
for (uint256 i = 0; i < networks.length; i++) {
UpdateNetworkInfo calldata info = networks[i];
bytes32 messageHash = info.eip712TypedHash(DOMAIN_SEPARATOR);
_setNetworkInfo(bytes32(uint256(uint160(_getAdmin()))), messageHash, info);
}
}

/**
* @dev Update network information
* @param signature Schnorr signature
Expand Down