Skip to content

Commit

Permalink
Merge pull request #2 from ExocoreNetwork/fix/v3-upgrade
Browse files Browse the repository at this point in the history
fix: v3 testnet upgrade findings
  • Loading branch information
MaxMustermann2 authored May 30, 2024
2 parents b834259 + 0b0a42c commit e36c4d0
Show file tree
Hide file tree
Showing 11 changed files with 440 additions and 36 deletions.
61 changes: 61 additions & 0 deletions script/10_DeployExocoreGatewayOnly.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
pragma solidity ^0.8.19;

import {ILayerZeroEndpointV2} from "@layerzero-v2/protocol/contracts/interfaces/ILayerZeroEndpointV2.sol";

import {ProxyAdmin} from "@openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

import {ExocoreGateway} from "../src/core/ExocoreGateway.sol";
import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";

contract DeployExocoreGatewayOnly is BaseScript {

function setUp() public virtual override {
// load keys
super.setUp();
// load contracts
string memory prerequisities = vm.readFile("script/prerequisiteContracts.json");
exocoreLzEndpoint = ILayerZeroEndpointV2(stdJson.readAddress(prerequisities, ".exocore.lzEndpoint"));
require(address(exocoreLzEndpoint) != address(0), "exocore l0 endpoint should not be empty");
// fork
exocore = vm.createSelectFork(exocoreRPCURL);
}

function run() public {
vm.selectFork(exocore);
vm.startBroadcast(deployer.privateKey);

ProxyAdmin exocoreProxyAdmin = new ProxyAdmin();
ExocoreGateway exocoreGatewayLogic = new ExocoreGateway(address(exocoreLzEndpoint));
exocoreGateway = ExocoreGateway(
payable(
address(
new TransparentUpgradeableProxy(
address(exocoreGatewayLogic),
address(exocoreProxyAdmin),
abi.encodeWithSelector(
exocoreGatewayLogic.initialize.selector,
payable(exocoreValidatorSet.addr)
)
)
)
)
);

vm.stopBroadcast();

string memory exocoreContracts = "exocoreContracts";
vm.serializeAddress(exocoreContracts, "lzEndpoint", address(exocoreLzEndpoint));
vm.serializeAddress(exocoreContracts, "exocoreGatewayLogic", address(exocoreGatewayLogic));
string memory exocoreContractsOutput =
vm.serializeAddress(exocoreContracts, "exocoreGateway", address(exocoreGateway));

string memory deployedContracts = "deployedContracts";
string memory finalJson =
vm.serializeString(deployedContracts, "exocore", exocoreContractsOutput);

vm.writeJson(finalJson, "script/deployedExocoreGatewayOnly.json");

}
}
97 changes: 97 additions & 0 deletions script/11_SetPeers.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
pragma solidity ^0.8.19;

import {ExocoreGateway} from "../src/core/ExocoreGateway.sol";
import {Bootstrap} from "../src/core/Bootstrap.sol";

import {CLIENT_CHAINS_PRECOMPILE_ADDRESS} from "../src/interfaces/precompiles/IClientChains.sol";

import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";

import "@layerzero-v2/protocol/contracts/libs/AddressCast.sol";

contract SetPeersAndUpgrade is BaseScript {
using AddressCast for address;

address bootstrapAddr;
address exocoreGatewayAddr;

function setUp() public virtual override {
// load keys
super.setUp();
// load contracts
string memory deployed = vm.readFile("script/deployedBootstrapOnly.json");
bootstrapAddr = stdJson.readAddress(deployed, ".clientChain.bootstrap");
require(address(bootstrapAddr) != address(0), "bootstrap address should not be empty");
deployed = vm.readFile("script/deployedExocoreGatewayOnly.json");
exocoreGatewayAddr = stdJson.readAddress(deployed, ".exocore.exocoreGateway");
require(address(exocoreGatewayAddr) != address(0), "exocore gateway address should not be empty");
// forks
exocore = vm.createSelectFork(exocoreRPCURL);
clientChain = vm.createSelectFork(clientChainRPCURL);
}

function run() public {
ExocoreGateway gateway = ExocoreGateway(payable(exocoreGatewayAddr));

vm.selectFork(exocore);
vm.startBroadcast(exocoreValidatorSet.privateKey);
gateway.setPeer(clientChainId, bootstrapAddr.toBytes32());
vm.stopBroadcast();

Bootstrap bootstrap = Bootstrap(payable(bootstrapAddr));

vm.selectFork(clientChain);
vm.startBroadcast(exocoreValidatorSet.privateKey);
bootstrap.setPeer(exocoreChainId, address(exocoreGatewayAddr).toBytes32());
vm.stopBroadcast();

// check that peer is set (we run with --slow but even then there's some risk)
uint256 i = 0;
uint256 tries = 5;
bool success;
while(i < tries) {

vm.selectFork(exocore);
success = gateway.peers(clientChainId) == bootstrapAddr.toBytes32();

vm.selectFork(clientChain);
success = success && bootstrap.peers(exocoreChainId) == address(exocoreGatewayAddr).toBytes32();

if (success) {
break;
}

i++;
}
require(i < tries, "peers not set");

// the upgrade does not work via script due to the precompile issue
// https://github.com/ExocoreNetwork/exocore/issues/78
// // now that peers are set, we should upgrade the Bootstrap contract via gateway
// // but first allow simulation to run
// vm.selectFork(exocore);
// bytes memory mockCode = vm.getDeployedCode("ClientChainsMock.sol");
// vm.etch(CLIENT_CHAINS_PRECOMPILE_ADDRESS, mockCode);

// console.log("clientChainId", clientChainId);
// vm.startBroadcast(exocoreValidatorSet.privateKey);
// // fund the gateway
// if (exocoreGatewayAddr.balance < 1 ether) {
// (bool sent,) = exocoreGatewayAddr.call{value: 1 ether}("");
// require(sent, "Failed to send Ether");
// }
// // gateway.markBootstrapOnAllChains();

// instruct the user to upgrade manually
// this can be done even without calling x/assets UpdateParams
// because that parameter is not involved in this process.
console.log("Cross-chain upgrade command:");
console.log(
"source .env && cast send --rpc-url $EXOCORE_TESETNET_RPC",
exocoreGatewayAddr,
"\"markBootstrapOnAllChains()\"",
"--private-key $TEST_ACCOUNT_THREE_PRIVATE_KEY"
);
}
}
88 changes: 88 additions & 0 deletions script/12_RedeployClientChainGateway.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
pragma solidity ^0.8.19;

import {TransparentUpgradeableProxy} from "@openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {UpgradeableBeacon} from "@openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol";

import {Bootstrap} from "../src/core/Bootstrap.sol";
import {ClientChainGateway} from "../src/core/ClientChainGateway.sol";
import {CustomProxyAdmin} from "../src/core/CustomProxyAdmin.sol";
import {Vault} from "../src/core/Vault.sol";
import "../src/core/BeaconProxyBytecode.sol";
import "../src/core/ExoCapsule.sol";

import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";
import {ILayerZeroEndpointV2} from "@layerzero-v2/protocol/contracts/interfaces/ILayerZeroEndpointV2.sol";
import {ERC20PresetFixedSupply} from "@openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol";
import "@beacon-oracle/contracts/src/EigenLayerBeaconOracle.sol";

contract RedeployClientChainGateway is BaseScript {
Bootstrap bootstrap;

function setUp() public virtual override {
// load keys
super.setUp();
// load contracts
string memory prerequisiteContracts = vm.readFile("script/deployedBootstrapOnly.json");
clientChainLzEndpoint = ILayerZeroEndpointV2(
stdJson.readAddress(prerequisiteContracts, ".clientChain.lzEndpoint")
);
require(address(clientChainLzEndpoint) != address(0), "client chain l0 endpoint should not be empty");
beaconOracle = EigenLayerBeaconOracle(
stdJson.readAddress(prerequisiteContracts, ".clientChain.beaconOracle")
);
require(address(beaconOracle) != address(0), "beacon oracle should not be empty");
vaultBeacon = UpgradeableBeacon(
stdJson.readAddress(prerequisiteContracts, ".clientChain.vaultBeacon")
);
require(address(vaultBeacon) != address(0), "vault beacon should not be empty");
capsuleBeacon = UpgradeableBeacon(
stdJson.readAddress(prerequisiteContracts, ".clientChain.capsuleBeacon")
);
require(address(capsuleBeacon) != address(0), "capsule beacon should not be empty");
beaconProxyBytecode = BeaconProxyBytecode(
stdJson.readAddress(prerequisiteContracts, ".clientChain.beaconProxyBytecode")
);
require(address(beaconProxyBytecode) != address(0), "beacon proxy bytecode should not be empty");
bootstrap = Bootstrap(
stdJson.readAddress(prerequisiteContracts, ".clientChain.bootstrap")
);
require(address(bootstrap) != address(0), "bootstrap should not be empty");
clientChain = vm.createSelectFork(clientChainRPCURL);
}

function run() public {
vm.selectFork(clientChain);
vm.startBroadcast(exocoreValidatorSet.privateKey);
ClientChainGateway clientGatewayLogic = new ClientChainGateway(
address(clientChainLzEndpoint),
exocoreChainId,
address(beaconOracle),
address(vaultBeacon),
address(capsuleBeacon),
address(beaconProxyBytecode)
);
// then the client chain initialization
address[] memory emptyList;
bytes memory initialization = abi.encodeWithSelector(
clientGatewayLogic.initialize.selector,
exocoreValidatorSet.addr,
emptyList
);
bootstrap.setClientChainGatewayLogic(
address(clientGatewayLogic),
initialization
);
vm.stopBroadcast();

string memory clientChainContracts = "clientChainContracts";
string memory clientChainContractsOutput =
vm.serializeAddress(clientChainContracts, "clientGatewayLogic", address(clientGatewayLogic));

string memory deployedContracts = "deployedContracts";
string memory finalJson =
vm.serializeString(deployedContracts, "clientChain", clientChainContractsOutput);

vm.writeJson(finalJson, "script/redeployClientChainGateway.json");
}
}
100 changes: 100 additions & 0 deletions script/8_RegisterOperatorsAndDelegate.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
pragma solidity ^0.8.19;

import {Bootstrap} from "../src/core/Bootstrap.sol";
import {Vault} from "../src/core/Vault.sol";
import {IOperatorRegistry} from "../src/interfaces/IOperatorRegistry.sol";

import {ERC20PresetFixedSupply} from "@openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol";

import "forge-std/Script.sol";

// This script does not intentionally inherit from BaseScript, since
// that script has boilerplate that is not needed here.
contract RegisterOperatorsAndDelegate is Script {
// registration data for operators
uint256[] operatorKeys;
string[] exoAddresses;
string[] names;
bytes32[] consKeys;
// rpc settings
string clientChainRPCURL;
uint256 clientChain;
// addresses of contracts
address bootstrapAddr;
address tokenAddr;
// each subarray sums to deposits, and each item is the delegation amount
uint256[4][4] amounts = [
[ 1500 * 1e18, 250 * 1e18, 250 * 1e18, 0 * 1e18 ],
[ 300 * 1e18, 1500 * 1e18, 0 * 1e18, 200 * 1e18 ],
[ 0 * 1e18, 0 * 1e18, 2500 * 1e18, 500 * 1e18 ],
[ 1000 * 1e18, 0 * 1e18, 0 * 1e18, 2000 * 1e18 ]
];

function setUp() public {
operatorKeys = vm.envUint("OPERATOR_KEYS", ",");
exoAddresses = vm.envString("EXO_ADDRESSES", ",");
names = vm.envString("NAMES", ",");
consKeys = vm.envBytes32("CONS_KEYS", ",");

clientChainRPCURL = vm.envString("SEPOLIA_RPC");
clientChain = vm.createSelectFork(clientChainRPCURL);

require(
operatorKeys.length == exoAddresses.length &&
operatorKeys.length == names.length &&
operatorKeys.length == consKeys.length,
"Operator registration data length mismatch"
);

string memory deployedContracts = vm.readFile("script/deployedBootstrapOnly.json");
bootstrapAddr = stdJson.readAddress(deployedContracts, ".clientChain.bootstrap");
require(bootstrapAddr != address(0), "Bootstrap address should not be empty");
tokenAddr = stdJson.readAddress(deployedContracts, ".clientChain.erc20Token");
require(tokenAddr != address(0), "Token address should not be empty");
}

function run() public {
vm.selectFork(clientChain);
IOperatorRegistry.Commission memory commission = IOperatorRegistry.Commission(
0, 1e18, 1e18
);
Bootstrap bootstrap = Bootstrap(bootstrapAddr);
ERC20PresetFixedSupply token = ERC20PresetFixedSupply(tokenAddr);
address vaultAddr = address(bootstrap.tokenToVault(tokenAddr));
for(uint256 i = 0; i < operatorKeys.length; i++) {
uint256 pk = operatorKeys[i];
// address addr = vm.addr(pk);
string memory exoAddr = exoAddresses[i];
string memory name = names[i];
bytes32 consKey = consKeys[i];
vm.startBroadcast(pk);
// register operator
bootstrap.registerOperator(
exoAddr, name, commission, consKey
);
uint256 depositAmount = 0;
for(uint256 j = 0; j < amounts[i].length; j++) {
depositAmount += amounts[i][j];
}
// approve
token.approve(vaultAddr, type(uint256).max);
// transfer
bootstrap.deposit(tokenAddr, depositAmount);
vm.stopBroadcast();
}
for(uint256 i = 0; i < operatorKeys.length; i++) {
uint256 pk = operatorKeys[i];
vm.startBroadcast(pk);
for(uint256 j = 0; j < operatorKeys.length; j++) {
uint256 amount = amounts[i][j];
if (amount == 0) {
continue;
}
// i is the transaction sender and j is the operator
string memory exoAddr = exoAddresses[j];
bootstrap.delegateTo(exoAddr, tokenAddr, amount);
}
vm.stopBroadcast();
}
}
}
30 changes: 30 additions & 0 deletions script/9_ExtendBootstrapTime.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
pragma solidity ^0.8.19;

import {Bootstrap} from "../src/core/Bootstrap.sol";

import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";

contract SetBootstrapTime is BaseScript {
address bootstrapAddr;

function setUp() public virtual override {
// load keys
super.setUp();
// load contracts
string memory deployedContracts = vm.readFile("script/deployedBootstrapOnly.json");
bootstrapAddr = stdJson.readAddress(deployedContracts, ".clientChain.bootstrap");

clientChain = vm.createSelectFork(clientChainRPCURL);
}

function run() public {
vm.selectFork(clientChain);
vm.startBroadcast(exocoreValidatorSet.privateKey);

Bootstrap bootstrap = Bootstrap(bootstrapAddr);
bootstrap.setSpawnTime(block.timestamp + 120 seconds);

vm.stopBroadcast();
}
}
Loading

0 comments on commit e36c4d0

Please sign in to comment.