From 4e21bef3e1f94202d55ccd7c45a93b90a29a29b8 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 29 May 2024 17:32:53 +0000 Subject: [PATCH 1/6] feat(generate): do not overwrite existing file --- script/generate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/generate.js b/script/generate.js index 3ce89937..8b52d42f 100644 --- a/script/generate.js +++ b/script/generate.js @@ -37,7 +37,7 @@ const isValidBech32 = (address) => { // Load variables from .env file -const { SEPOLIA_RPC, BOOTSTRAP_ADDRESS, BASE_GENESIS_FILE_PATH, EXCHANGE_RATES } = process.env; +const { SEPOLIA_RPC, BOOTSTRAP_ADDRESS, BASE_GENESIS_FILE_PATH, RESULT_GENESIS_FILE_PATH, EXCHANGE_RATES } = process.env; async function updateGenesisFile() { try { @@ -407,7 +407,7 @@ async function updateGenesisFile() { }); genesisJSON.app_state.delegation.delegations = baseLevel; - await fs.writeFile(BASE_GENESIS_FILE_PATH, JSON.stringify(genesisJSON, null, 2)); + await fs.writeFile(RESULT_GENESIS_FILE_PATH, JSON.stringify(genesisJSON, null, 2)); console.log('Genesis file updated successfully.'); } catch (error) { console.error('Error updating genesis file:', error.message); From 21f7c040471b1ad68cf6b1c5c16f8c00a1420484 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 29 May 2024 17:33:26 +0000 Subject: [PATCH 2/6] fix(client-gateway): avoid out of gas errors Looping through the arrays is too expensive. We will have to forego the gas refund. See, for example, action 8 in Parity trace of Sepolia tx 0xdfe3e77db09a0951bb9b6b9d7cb5d92211022a34290cf1477cf7a79c8f98febc, which shows "out of gas". --- src/core/ClientChainGateway.sol | 66 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/core/ClientChainGateway.sol b/src/core/ClientChainGateway.sol index 49a5a902..847261a1 100644 --- a/src/core/ClientChainGateway.sol +++ b/src/core/ClientChainGateway.sol @@ -103,39 +103,39 @@ contract ClientChainGateway is // no risk keeping these but they are cheap to clear. delete exocoreSpawnTime; delete offsetDuration; - // TODO: are these loops even worth it? the maximum refund is 50% of the gas cost. - // if not, we can remove them. - // the lines above this set of comments are at least cheaper to clear, - // and have no utility after initialization. - for(uint i = 0; i < depositors.length; i++) { - address depositor = depositors[i]; - for(uint j = 0; j < whitelistTokens.length; j++) { - address token = whitelistTokens[j]; - delete totalDepositAmounts[depositor][token]; - delete withdrawableAmounts[depositor][token]; - for(uint k = 0; k < registeredOperators.length; k++) { - address eth = registeredOperators[k]; - string memory exo = ethToExocoreAddress[eth]; - delete delegations[depositor][exo][token]; - } - } - delete isDepositor[depositor]; - } - for(uint k = 0; k < registeredOperators.length; k++) { - address eth = registeredOperators[k]; - string memory exo = ethToExocoreAddress[eth]; - delete operators[exo]; - delete commissionEdited[exo]; - delete ethToExocoreAddress[eth]; - for(uint j = 0; j < whitelistTokens.length; j++) { - address token = whitelistTokens[j]; - delete delegationsByOperator[exo][token]; - } - } - for(uint j = 0; j < whitelistTokens.length; j++) { - address token = whitelistTokens[j]; - delete depositsByToken[token]; - } + // // TODO: are these loops even worth it? the maximum refund is 50% of the gas cost. + // // if not, we can remove them. + // // the lines above this set of comments are at least cheaper to clear, + // // and have no utility after initialization. + // for(uint i = 0; i < depositors.length; i++) { + // address depositor = depositors[i]; + // for(uint j = 0; j < whitelistTokens.length; j++) { + // address token = whitelistTokens[j]; + // delete totalDepositAmounts[depositor][token]; + // delete withdrawableAmounts[depositor][token]; + // for(uint k = 0; k < registeredOperators.length; k++) { + // address eth = registeredOperators[k]; + // string memory exo = ethToExocoreAddress[eth]; + // delete delegations[depositor][exo][token]; + // } + // } + // delete isDepositor[depositor]; + // } + // for(uint k = 0; k < registeredOperators.length; k++) { + // address eth = registeredOperators[k]; + // string memory exo = ethToExocoreAddress[eth]; + // delete operators[exo]; + // delete commissionEdited[exo]; + // delete ethToExocoreAddress[eth]; + // for(uint j = 0; j < whitelistTokens.length; j++) { + // address token = whitelistTokens[j]; + // delete delegationsByOperator[exo][token]; + // } + // } + // for(uint j = 0; j < whitelistTokens.length; j++) { + // address token = whitelistTokens[j]; + // delete depositsByToken[token]; + // } // these should also be cleared - even if the loops are not used // cheap to clear and potentially large in size. delete depositors; From 6fac2fa4441ee7b34980d29fcdc67c86aa4cace9 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 29 May 2024 17:35:17 +0000 Subject: [PATCH 3/6] fix(mock): return testnet chain id --- test/mocks/ClientChainsMock.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocks/ClientChainsMock.sol b/test/mocks/ClientChainsMock.sol index 2528e774..26ab90b1 100644 --- a/test/mocks/ClientChainsMock.sol +++ b/test/mocks/ClientChainsMock.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import {IClientChains} from "../../src/interfaces/precompiles/IClientChains.sol"; contract ClientChainsMock is IClientChains { - uint16 clientChainId = 2; + uint16 clientChainId = 40161; function getClientChains() external view returns (bool, uint16[] memory) { uint16[] memory res = new uint16[](1); From 5e552cd1d4e370c6bb881b1757cb3b1d9351d5eb Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Wed, 29 May 2024 17:35:59 +0000 Subject: [PATCH 4/6] feat: add scripts from testnet upgrade v3 8 -> register operators to bootstrap contract 9 -> edit bootstrap time (DEPENDS on script content!) 10 -> deploy only exocore gateway on our chain 11 -> set peers on both sides (I couldn't get markBootstrap to work here so I had to `cast send` the transaction manually 12 -> redeploys the (modified) client chain gateway. since the previous upgrade transaction failed due to an out of gas error, the client chain gateway logic was modified to remove the loops during initialization, and redeployed. this script also calls `setClientChainGatewayLogic` appropriately. Also included are the contract addresses as JSON for each deployment script above. --- script/10_DeployExocoreGatewayOnly.s.sol | 61 +++++++++++++ script/11_SetPeers.s.sol | 83 +++++++++++++++++ script/12_RedeployClientChainGateway.s.sol | 88 ++++++++++++++++++ script/8_RegisterOperatorsAndDelegate.s.sol | 98 +++++++++++++++++++++ script/9_ExtendBootstrapTime.s.sol | 30 +++++++ script/deployedBootstrapOnly.json | 16 ++++ script/deployedExocoreGatewayOnly.json | 7 ++ script/redeployClientChainGateway.json | 5 ++ 8 files changed, 388 insertions(+) create mode 100644 script/10_DeployExocoreGatewayOnly.s.sol create mode 100644 script/11_SetPeers.s.sol create mode 100644 script/12_RedeployClientChainGateway.s.sol create mode 100644 script/8_RegisterOperatorsAndDelegate.s.sol create mode 100644 script/9_ExtendBootstrapTime.s.sol create mode 100644 script/deployedBootstrapOnly.json create mode 100644 script/deployedExocoreGatewayOnly.json create mode 100644 script/redeployClientChainGateway.json diff --git a/script/10_DeployExocoreGatewayOnly.s.sol b/script/10_DeployExocoreGatewayOnly.s.sol new file mode 100644 index 00000000..f976f126 --- /dev/null +++ b/script/10_DeployExocoreGatewayOnly.s.sol @@ -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"); + + } +} \ No newline at end of file diff --git a/script/11_SetPeers.s.sol b/script/11_SetPeers.s.sol new file mode 100644 index 00000000..2f2aff72 --- /dev/null +++ b/script/11_SetPeers.s.sol @@ -0,0 +1,83 @@ +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; + bool success; + while(i <= 1e18) { + + 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 <= 1e18, "peers not set"); + + // 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(); + } +} \ No newline at end of file diff --git a/script/12_RedeployClientChainGateway.s.sol b/script/12_RedeployClientChainGateway.s.sol new file mode 100644 index 00000000..fa04e4ec --- /dev/null +++ b/script/12_RedeployClientChainGateway.s.sol @@ -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"); + } +} \ No newline at end of file diff --git a/script/8_RegisterOperatorsAndDelegate.s.sol b/script/8_RegisterOperatorsAndDelegate.s.sol new file mode 100644 index 00000000..862f4f25 --- /dev/null +++ b/script/8_RegisterOperatorsAndDelegate.s.sol @@ -0,0 +1,98 @@ +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"; + +contract DeployBootstrapOnly is Script { + // registration data for operators + uint256[] operatorKeys; + string[] exoAddresses; + string[] names; + bytes32[] consKeys; + // rpc settings + string clientChainRPCURL; + uint256 clientChainFork; + // 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("CLIENT_CHAIN_URL"); + clientChainFork = 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(clientChainFork); + 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(); + } + } +} \ No newline at end of file diff --git a/script/9_ExtendBootstrapTime.s.sol b/script/9_ExtendBootstrapTime.s.sol new file mode 100644 index 00000000..70b73827 --- /dev/null +++ b/script/9_ExtendBootstrapTime.s.sol @@ -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(); + } +} \ No newline at end of file diff --git a/script/deployedBootstrapOnly.json b/script/deployedBootstrapOnly.json new file mode 100644 index 00000000..01935b82 --- /dev/null +++ b/script/deployedBootstrapOnly.json @@ -0,0 +1,16 @@ +{ + "clientChain": { + "beaconOracle": "0xd3D285cd1516038dAED61B8BF7Ae2daD63662492", + "beaconProxyBytecode": "0xA15Ce26ba8E50ac21ecDa1791BAa3bf22a95b575", + "bootstrap": "0x53E91EB5105ec8C1c22055F790616cB8F82c664e", + "bootstrapLogic": "0x417CaBa1E4a63D1202dCc6E19F7c3eC79b31EC45", + "capsuleBeacon": "0xe87e516C7116eC4DcFC5408c05618De6e1Cd4c10", + "capsuleImplementation": "0xBe26AfFF7EC33d6F4BC8175d3F6f404692e82443", + "clientGatewayLogic": "0xcE10583b1Efe34319812d96c7edFFD71E8403ba2", + "erc20Token": "0x83E6850591425e3C1E263c054f4466838B9Bd9e4", + "lzEndpoint": "0x6EDCE65403992e310A62460808c4b910D972f10f", + "proxyAdmin": "0xE9591d5b1Ea9733ad36834cd0bDe40ce0028AE33", + "vaultBeacon": "0x2899181D6EB55847165cfa3288E4708dA5070751", + "vaultImplementation": "0xF22097E6799DF7D8b25CCeF6E64DA3CB9133012D" + } +} \ No newline at end of file diff --git a/script/deployedExocoreGatewayOnly.json b/script/deployedExocoreGatewayOnly.json new file mode 100644 index 00000000..2d038ade --- /dev/null +++ b/script/deployedExocoreGatewayOnly.json @@ -0,0 +1,7 @@ +{ + "exocore": { + "exocoreGateway": "0xe13Ef2fE9B4bC1A3bBB62Df6bB19d6aD79525036", + "exocoreGatewayLogic": "0x617c588c3FaAA105cec3438D0c031E143A8B23fd", + "lzEndpoint": "0x6EDCE65403992e310A62460808c4b910D972f10f" + } +} \ No newline at end of file diff --git a/script/redeployClientChainGateway.json b/script/redeployClientChainGateway.json new file mode 100644 index 00000000..c450ae21 --- /dev/null +++ b/script/redeployClientChainGateway.json @@ -0,0 +1,5 @@ +{ + "clientChain": { + "clientGatewayLogic": "0xdC51F6d62ce78EfF7c98f3BD59227B4D0785C6ef" + } +} \ No newline at end of file From aef624e022da6339dd59000959c50747d32f7f81 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Thu, 30 May 2024 08:11:54 +0000 Subject: [PATCH 5/6] fix(script): 5 tries to verify + manual upgrade Until the precompile issue is resolved, this script can print out a command that can be executed. The command will issue the upgrade command to the Bootstrap contract on the client chain. --- script/11_SetPeers.s.sol | 48 ++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/script/11_SetPeers.s.sol b/script/11_SetPeers.s.sol index 2f2aff72..c8dac6e5 100644 --- a/script/11_SetPeers.s.sol +++ b/script/11_SetPeers.s.sol @@ -48,8 +48,9 @@ contract SetPeersAndUpgrade is BaseScript { // 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 <= 1e18) { + while(i < tries) { vm.selectFork(exocore); success = gateway.peers(clientChainId) == bootstrapAddr.toBytes32(); @@ -63,21 +64,34 @@ contract SetPeersAndUpgrade is BaseScript { i++; } - require(i <= 1e18, "peers not set"); - - // 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(); + 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" + ); } } \ No newline at end of file From 0b0a42cb3fc503f39546225ef1afd2ce31ea216a Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Thu, 30 May 2024 08:13:25 +0000 Subject: [PATCH 6/6] fix(script): use same env var for rpc url Even though the script 8 does not inherit from `BaseScript`, the environment variable for the client chain RPC URL is the same in both. --- script/8_RegisterOperatorsAndDelegate.s.sol | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/script/8_RegisterOperatorsAndDelegate.s.sol b/script/8_RegisterOperatorsAndDelegate.s.sol index 862f4f25..b9621a39 100644 --- a/script/8_RegisterOperatorsAndDelegate.s.sol +++ b/script/8_RegisterOperatorsAndDelegate.s.sol @@ -8,7 +8,9 @@ import {ERC20PresetFixedSupply} from "@openzeppelin-contracts/contracts/token/ER import "forge-std/Script.sol"; -contract DeployBootstrapOnly is Script { +// 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; @@ -16,7 +18,7 @@ contract DeployBootstrapOnly is Script { bytes32[] consKeys; // rpc settings string clientChainRPCURL; - uint256 clientChainFork; + uint256 clientChain; // addresses of contracts address bootstrapAddr; address tokenAddr; @@ -34,8 +36,8 @@ contract DeployBootstrapOnly is Script { names = vm.envString("NAMES", ","); consKeys = vm.envBytes32("CONS_KEYS", ","); - clientChainRPCURL = vm.envString("CLIENT_CHAIN_URL"); - clientChainFork = vm.createSelectFork(clientChainRPCURL); + clientChainRPCURL = vm.envString("SEPOLIA_RPC"); + clientChain = vm.createSelectFork(clientChainRPCURL); require( operatorKeys.length == exoAddresses.length && @@ -52,7 +54,7 @@ contract DeployBootstrapOnly is Script { } function run() public { - vm.selectFork(clientChainFork); + vm.selectFork(clientChain); IOperatorRegistry.Commission memory commission = IOperatorRegistry.Commission( 0, 1e18, 1e18 );