From 658740a3498955198dc0a542e6e69899f8a71620 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:39:16 +0200 Subject: [PATCH 01/10] docs: Fix path in readme --- docs/soldoc/src/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/soldoc/src/README.md b/docs/soldoc/src/README.md index f23437fc..5667e3eb 100644 --- a/docs/soldoc/src/README.md +++ b/docs/soldoc/src/README.md @@ -5,7 +5,7 @@ This project implements a cross-chain token bridge system for the RLC token usin ## Diagrams and source code docs (soldocs): - [Diagrams](../../diagrams/) - - [Source code docs](../../soldoc/) + - [Source code docs](../../soldoc/src/SUMMARY.md) ## Architecture From 978e2c9b28444938dc25750cebe33dbc8076748f Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:44:41 +0200 Subject: [PATCH 02/10] feat: Restore implems deployment using CreateX --- script/lib/UUPSProxyDeployer.sol | 30 ++++++++++++---------- test/e2e/IexecLayerZeroBridgeScript.t.sol | 10 ++++++++ test/units/RLCCrosschainTokenScript.t.sol | 8 ++++++ test/units/RLCLiquidityUnifierScript.t.sol | 8 ++++++ 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index 15f268da..b6da3778 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -17,12 +17,12 @@ library UUPSProxyDeployer { Vm private constant vm = StdConstants.VM; /** - * Deploys a UUPS proxy contract and its implementation using the CreateX Factory + * Deploys a UUPS proxy contract and its implementation in create2 mode using CreateX Factory. * @param contractName The name of the contract to deploy (used to fetch creation code) * @param constructorData The constructor arguments for the implementation contract * @param initializeData The initialization data for the proxy contract * @param createXFactory The address of the CreateX factory - * @param salt The salt for deterministic deployment + * @param createxSalt The salt for deterministic deployment * @return The address of the deployed proxy */ function deployUUPSProxyWithCreateX( @@ -30,12 +30,12 @@ library UUPSProxyDeployer { bytes memory constructorData, bytes memory initializeData, address createXFactory, - bytes32 salt + bytes32 createxSalt ) internal returns (address) { - ICreateX createX = ICreateX(createXFactory); - address implementation = deployImplementation(contractName, constructorData, createX); - address proxy = createX.deployCreate2AndInit( - salt, + address implementation = + deployImplementationUsingCreateX(contractName, constructorData, createXFactory, createxSalt); + address proxy = ICreateX(createXFactory).deployCreate2AndInit( + createxSalt, abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode initializeData, ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX @@ -45,18 +45,22 @@ library UUPSProxyDeployer { } /** - * Deploys the implementation contract using tradition `create`. + * Deploys the implementation contract in create2 mode using CreateX factory. * @param contractName The name of the contract to deploy (used to fetch creation code) * @param constructorData The constructor arguments for the implementation contract * @param createxFactory The address of the CreateX factory + * @param createxSalt The salt for deterministic deployment * @return The address of the deployed implementation contract */ - function deployImplementation(string memory contractName, bytes memory constructorData, ICreateX createxFactory) - internal - returns (address) - { + function deployImplementationUsingCreateX( + string memory contractName, + bytes memory constructorData, + address createxFactory, + bytes32 createxSalt + ) internal returns (address) { bytes memory creationCode = vm.getCode(contractName); - address implementation = createxFactory.deployCreate(abi.encodePacked(creationCode, constructorData)); + address implementation = + ICreateX(createxFactory).deployCreate2(createxSalt, abi.encodePacked(creationCode, constructorData)); console.log("Implementation deployed at:", implementation); return implementation; } diff --git a/test/e2e/IexecLayerZeroBridgeScript.t.sol b/test/e2e/IexecLayerZeroBridgeScript.t.sol index a9ae8026..a4a69be0 100644 --- a/test/e2e/IexecLayerZeroBridgeScript.t.sol +++ b/test/e2e/IexecLayerZeroBridgeScript.t.sol @@ -101,6 +101,16 @@ contract IexecLayerZeroBridgeScriptTest is Test { // TODO check that the proxy address is saved. } + function testFork_RevertWhen_TwoDeploymentsWithTheSameSalt() public { + deployer.deploy( + false, address(rlcCrosschainToken), params.lzEndpoint, admin, upgrader, pauser, params.createxFactory, salt + ); + vm.expectRevert(abi.encodeWithSignature("FailedContractCreation(address)", params.createxFactory)); + deployer.deploy( + false, address(rlcCrosschainToken), params.lzEndpoint, admin, upgrader, pauser, params.createxFactory, salt + ); + } + // TODO: add tests for the configuration script. function testFork_ConfigureContractCorrectly() public { diff --git a/test/units/RLCCrosschainTokenScript.t.sol b/test/units/RLCCrosschainTokenScript.t.sol index 1dd44490..9362a493 100644 --- a/test/units/RLCCrosschainTokenScript.t.sol +++ b/test/units/RLCCrosschainTokenScript.t.sol @@ -42,4 +42,12 @@ contract RLCCrosschainTokenTest is Test { // TODO check that the proxy address is saved. } + + // Makes sure create2 deployment is well implemented. + function test_RevertWhen_TwoDeploymentsWithTheSameSalt() public { + address random = makeAddr("random"); + deployer.deploy(name, symbol, admin, upgrader, createx, salt); + vm.expectRevert(abi.encodeWithSignature("FailedContractCreation(address)", createx)); + deployer.deploy("Foo", "BAR", random, random, createx, salt); + } } diff --git a/test/units/RLCLiquidityUnifierScript.t.sol b/test/units/RLCLiquidityUnifierScript.t.sol index d3161376..78f09b67 100644 --- a/test/units/RLCLiquidityUnifierScript.t.sol +++ b/test/units/RLCLiquidityUnifierScript.t.sol @@ -35,4 +35,12 @@ contract LiquidityUnifierTest is Test { rlcLiquidityUnifier.initialize(admin, upgrader); // TODO check that the proxy address is saved. } + + // Makes sure create2 deployment is well implemented. + function test_RevertWhen_TwoDeploymentsWithTheSameSalt() public { + address random = makeAddr("random"); + deployer.deploy(rlcToken, admin, upgrader, createx, salt); + vm.expectRevert(abi.encodeWithSignature("FailedContractCreation(address)", createx)); + deployer.deploy(rlcToken, random, random, createx, salt); + } } From a4270c511188e1266641cec8259f99513e0fa1ed Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:47:24 +0200 Subject: [PATCH 03/10] refactor: Rename function --- script/RLCCrosschainToken.s.sol | 4 +--- script/RLCLiquidityUnifier.s.sol | 2 +- script/bridges/layerZero/IexecLayerZeroBridge.s.sol | 2 +- script/lib/UUPSProxyDeployer.sol | 10 +++++----- test/units/utils/TestUtils.sol | 6 +++--- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 8c6b1724..07076e8f 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -58,8 +58,6 @@ contract Deploy is Script { ) public returns (address) { bytes memory initData = abi.encodeWithSelector(RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader); - return UUPSProxyDeployer.deployUUPSProxyWithCreateX( - "RLCCrosschainToken", "", initData, createxFactory, createxSalt - ); + return UUPSProxyDeployer.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); } } diff --git a/script/RLCLiquidityUnifier.s.sol b/script/RLCLiquidityUnifier.s.sol index 45388a2a..bb5a6147 100644 --- a/script/RLCLiquidityUnifier.s.sol +++ b/script/RLCLiquidityUnifier.s.sol @@ -57,7 +57,7 @@ contract Deploy is Script { bytes memory constructorData = abi.encode(rlcToken); bytes memory initData = abi.encodeWithSelector(RLCLiquidityUnifier.initialize.selector, initialAdmin, initialUpgrader); - return UUPSProxyDeployer.deployUUPSProxyWithCreateX( + return UUPSProxyDeployer.deployUsingCreateX( "RLCLiquidityUnifier", constructorData, initData, createxFactory, createxSalt ); } diff --git a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol index 1d9a514d..d6541999 100644 --- a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol +++ b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol @@ -53,7 +53,7 @@ contract Deploy is Script { bytes memory initializeData = abi.encodeWithSelector( IexecLayerZeroBridge.initialize.selector, initialAdmin, initialUpgrader, initialPauser ); - return UUPSProxyDeployer.deployUUPSProxyWithCreateX( + return UUPSProxyDeployer.deployUsingCreateX( "IexecLayerZeroBridge", constructorData, initializeData, createxFactory, createxSalt ); } diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index b6da3778..9dfeb5b8 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -21,20 +21,20 @@ library UUPSProxyDeployer { * @param contractName The name of the contract to deploy (used to fetch creation code) * @param constructorData The constructor arguments for the implementation contract * @param initializeData The initialization data for the proxy contract - * @param createXFactory The address of the CreateX factory + * @param createxFactory The address of the CreateX factory * @param createxSalt The salt for deterministic deployment * @return The address of the deployed proxy */ - function deployUUPSProxyWithCreateX( + function deployUsingCreateX( string memory contractName, bytes memory constructorData, bytes memory initializeData, - address createXFactory, + address createxFactory, bytes32 createxSalt ) internal returns (address) { address implementation = - deployImplementationUsingCreateX(contractName, constructorData, createXFactory, createxSalt); - address proxy = ICreateX(createXFactory).deployCreate2AndInit( + deployImplementationUsingCreateX(contractName, constructorData, createxFactory, createxSalt); + address proxy = ICreateX(createxFactory).deployCreate2AndInit( createxSalt, abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode initializeData, diff --git a/test/units/utils/TestUtils.sol b/test/units/utils/TestUtils.sol index 0450b5e4..88671f8b 100644 --- a/test/units/utils/TestUtils.sol +++ b/test/units/utils/TestUtils.sol @@ -45,7 +45,7 @@ library TestUtils { // Deploy Liquidity Unifier rlcLiquidityUnifier = RLCLiquidityUnifier( - UUPSProxyDeployer.deployUUPSProxyWithCreateX( + UUPSProxyDeployer.deployUsingCreateX( "RLCLiquidityUnifier", abi.encode(rlcToken), abi.encodeWithSelector(RLCLiquidityUnifier.initialize.selector, initialAdmin, initialUpgrader), @@ -56,7 +56,7 @@ library TestUtils { // Deploy IexecLayerZeroBridgeAdapter iexecLayerZeroBridgeChainA = IexecLayerZeroBridge( - UUPSProxyDeployer.deployUUPSProxyWithCreateX( + UUPSProxyDeployer.deployUsingCreateX( "IexecLayerZeroBridge", abi.encode(true, rlcLiquidityUnifier, lzEndpointSource), abi.encodeWithSelector( @@ -75,7 +75,7 @@ library TestUtils { ); // Deploy IexecLayerZeroBridge iexecLayerZeroBridgeChainB = IexecLayerZeroBridge( - UUPSProxyDeployer.deployUUPSProxyWithCreateX( + UUPSProxyDeployer.deployUsingCreateX( "IexecLayerZeroBridge", abi.encode(false, rlcCrosschainToken, lzEndpointDestination), abi.encodeWithSelector( From b8ff5c72ecdf4ec7ca5982e96846a58cb91acd69 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Mon, 7 Jul 2025 20:24:53 +0200 Subject: [PATCH 04/10] refactor: Move mock files to test folder --- script/bridges/layerZero/IexecLayerZeroBridge.s.sol | 7 +++++++ test/units/RLCLiquidityUnifierUpgrade.t.sol | 2 +- .../bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol | 2 +- {src => test/units}/mocks/IexecLayerZeroBridgeV2Mock.sol | 2 +- {src => test/units}/mocks/RLCLiquidityUnifierV2Mock.sol | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) rename {src => test/units}/mocks/IexecLayerZeroBridgeV2Mock.sol (92%) rename {src => test/units}/mocks/RLCLiquidityUnifierV2Mock.sol (93%) diff --git a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol index d6541999..e58dfe37 100644 --- a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol +++ b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol @@ -59,6 +59,13 @@ contract Deploy is Script { } } +/** + * This script is used to configure the IexecLayerZeroBridge contract on both source + * and target chains. + * It sets required LayerZero bridge config: peer address and enforced options. + * It also grants the bridge the necessary roles in the RLCCrosschainToken contract + * or RLCLiquidityUnifier contract, depending on the configuration. + */ contract Configure is Script { using OptionsBuilder for bytes; diff --git a/test/units/RLCLiquidityUnifierUpgrade.t.sol b/test/units/RLCLiquidityUnifierUpgrade.t.sol index ffa3412d..20fba3db 100644 --- a/test/units/RLCLiquidityUnifierUpgrade.t.sol +++ b/test/units/RLCLiquidityUnifierUpgrade.t.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.22; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; -import {RLCLiquidityUnifierV2} from "../../src/mocks/RLCLiquidityUnifierV2Mock.sol"; import {RLCLiquidityUnifier} from "../../src/RLCLiquidityUnifier.sol"; import {TestUtils} from "./utils/TestUtils.sol"; import {UpgradeUtils} from "../../script/lib/UpgradeUtils.sol"; import {RLCMock} from "./mocks/RLCMock.sol"; +import {RLCLiquidityUnifierV2} from "./mocks/RLCLiquidityUnifierV2Mock.sol"; contract RLCLiquidityUnifierUpgradeTest is TestHelperOz5 { using TestUtils for *; diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol index ceec7cac..b0fe7d43 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.22; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; -import {IexecLayerZeroBridgeV2} from "../../../../src/mocks/IexecLayerZeroBridgeV2Mock.sol"; +import {IexecLayerZeroBridgeV2} from "../../mocks/IexecLayerZeroBridgeV2Mock.sol"; import {TestUtils} from "./../../utils/TestUtils.sol"; import {UpgradeUtils} from "../../../../script/lib/UpgradeUtils.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; diff --git a/src/mocks/IexecLayerZeroBridgeV2Mock.sol b/test/units/mocks/IexecLayerZeroBridgeV2Mock.sol similarity index 92% rename from src/mocks/IexecLayerZeroBridgeV2Mock.sol rename to test/units/mocks/IexecLayerZeroBridgeV2Mock.sol index 6ee77705..019d1f56 100644 --- a/src/mocks/IexecLayerZeroBridgeV2Mock.sol +++ b/test/units/mocks/IexecLayerZeroBridgeV2Mock.sol @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.22; -import {IexecLayerZeroBridge} from "../bridges/layerZero/IexecLayerZeroBridge.sol"; +import {IexecLayerZeroBridge} from "../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; /** * @title IexecLayerZeroBridgeV2 - V2 implementation with additional features diff --git a/src/mocks/RLCLiquidityUnifierV2Mock.sol b/test/units/mocks/RLCLiquidityUnifierV2Mock.sol similarity index 93% rename from src/mocks/RLCLiquidityUnifierV2Mock.sol rename to test/units/mocks/RLCLiquidityUnifierV2Mock.sol index 90644e20..2814bf5b 100644 --- a/src/mocks/RLCLiquidityUnifierV2Mock.sol +++ b/test/units/mocks/RLCLiquidityUnifierV2Mock.sol @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.22; -import {RLCLiquidityUnifier} from "../RLCLiquidityUnifier.sol"; +import {RLCLiquidityUnifier} from "../../../src/RLCLiquidityUnifier.sol"; /** * @title RLCLiquidityUnifierV2 - V2 implementation with additional features From 9dfe14f35fc03b3cffe361d7b5d2b7778ac388fb Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Mon, 7 Jul 2025 20:39:19 +0200 Subject: [PATCH 05/10] feat: Add TODO --- script/RLCCrosschainToken.s.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 07076e8f..a3c84b4b 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -61,3 +61,5 @@ contract Deploy is Script { return UUPSProxyDeployer.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); } } + +// TODO add upgrade script. From 4162738f300fb1a4f4e9815d763a59f9fcd8dd28 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:43:05 +0200 Subject: [PATCH 06/10] refactor: Clean upgrade scripts --- Makefile | 46 ------------------ script/RLCCrosschainToken.s.sol | 14 +++++- script/RLCLiquidityUnifier.s.sol | 14 ++---- .../layerZero/IexecLayerZeroBridge.s.sol | 19 ++------ script/lib/UpgradeUtils.sol | 48 ++++++------------- test/units/RLCLiquidityUnifierUpgrade.t.sol | 10 ++-- .../IexecLayerZeroBridgeUpgrade.t.sol | 9 ++-- 7 files changed, 43 insertions(+), 117 deletions(-) diff --git a/Makefile b/Makefile index e4af1767..b936df0a 100644 --- a/Makefile +++ b/Makefile @@ -77,32 +77,6 @@ deploy-all: # SOURCE_CHAIN, SOURCE_RPC, TARGET_CHAIN, TARGET_RPC, OPTIONS @echo "⚠️ Please authorize bridges on RLCLiquidityUnifier and RLCCrosschainToken contracts." # TODO verify contracts after deployment. -# -# High-level upgrade targets -# - -upgrade-on-anvil: - $(MAKE) upgrade-all \ - SOURCE_CHAIN=sepolia SOURCE_RPC=$(ANVIL_SEPOLIA_RPC_URL) \ - TARGET_CHAIN=arbitrum_sepolia TARGET_RPC=$(ANVIL_ARBITRUM_SEPOLIA_RPC_URL) - -upgrade-on-mainnets: - $(MAKE) upgrade-all \ - SOURCE_CHAIN=ethereum SOURCE_RPC=$(ETHEREUM_RPC_URL) \ - TARGET_CHAIN=arbitrum TARGET_RPC=$(ARBITRUM_RPC_URL) \ - OPTIONS=--verify - -# TODO : RLCMultichain and RLCLiquidityUnifier upgrades -upgrade-on-testnets: - $(MAKE) upgrade-all \ - SOURCE_CHAIN=sepolia SOURCE_RPC=$(SEPOLIA_RPC_URL) \ - TARGET_CHAIN=arbitrum_sepolia TARGET_RPC=$(ARBITRUM_SEPOLIA_RPC_URL) \ - OPTIONS=--verify - -upgrade-all: # SOURCE_CHAIN, SOURCE_RPC, TARGET_CHAIN, TARGET_RPC, OPTIONS - $(MAKE) upgrade-contract CONTRACT=bridges/layerZero/IexecLayerZeroBridge CHAIN=$(SOURCE_CHAIN) RPC_URL=$(SOURCE_RPC) OPTIONS=$(OPTIONS) - $(MAKE) upgrade-contract CONTRACT=bridges/layerZero/IexecLayerZeroBridge CHAIN=$(TARGET_CHAIN) RPC_URL=$(TARGET_RPC) OPTIONS=$(OPTIONS) - # # Generic deployment targets # @@ -116,19 +90,6 @@ deploy-contract: # CONTRACT, CHAIN, RPC_URL, OPTIONS --broadcast \ -vvv -# -# Generic upgrade targets -# - -upgrade-contract: # CONTRACT, CHAIN, RPC_URL, OPTIONS - @echo "Upgrading $(CONTRACT) on $(CHAIN) with options: $(OPTIONS)" - CHAIN=$(CHAIN) forge script script/$(CONTRACT).s.sol:Upgrade \ - --rpc-url $(RPC_URL) \ - --account $(ACCOUNT) \ - --broadcast \ - $(OPTIONS) \ - -vvv - # # Generic configuration targets # @@ -142,13 +103,6 @@ configure-bridge: # SOURCE_CHAIN, TARGET_CHAIN, RPC_URL --broadcast \ -vvv -# -# Individual upgrade targets -# - -upgrade-layerzero-bridge: # CHAIN, RPC_URL - $(MAKE) upgrade-contract CONTRACT=bridges/layerZero/IexecLayerZeroBridge CHAIN=$(CHAIN) RPC_URL=$(RPC_URL) - # # Bridge operations. # diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index a3c84b4b..3c6f773e 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -8,6 +8,7 @@ import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; import {UUPSProxyDeployer} from "./lib/UUPSProxyDeployer.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; +import {UpgradeUtils} from "./lib/UpgradeUtils.sol"; /** * Deployment script for the RLCCrosschainToken contract. @@ -62,4 +63,15 @@ contract Deploy is Script { } } -// TODO add upgrade script. +contract Upgrade is Script { + function run() external { + vm.startBroadcast(); + UpgradeUtils.executeUpgrade({ + proxyAddress: address(0), // Replace with the actual proxy address + contractName: "", // e.g., "ContractV2.sol:ContractV2" + constructorData: new bytes(0), // Replace with the actual constructor data + initData: new bytes(0) // Replace with the actual initialization data + }); + vm.stopBroadcast(); + } +} diff --git a/script/RLCLiquidityUnifier.s.sol b/script/RLCLiquidityUnifier.s.sol index bb5a6147..99db4ede 100644 --- a/script/RLCLiquidityUnifier.s.sol +++ b/script/RLCLiquidityUnifier.s.sol @@ -65,17 +65,13 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { - string memory chain = vm.envString("CHAIN"); - ConfigLib.CommonConfigParams memory commonParams = ConfigLib.readCommonConfig(chain); - vm.startBroadcast(); - UpgradeUtils.UpgradeParams memory params = UpgradeUtils.UpgradeParams({ - proxyAddress: commonParams.rlcLiquidityUnifierAddress, - constructorData: abi.encode(commonParams.rlcToken), - contractName: "RLCLiquidityUnifierV2Mock.sol:RLCLiquidityUnifierV2", // Would be production contract in real deployment - newStateVariable: 1000000 * 10 ** 9 + UpgradeUtils.executeUpgrade({ + proxyAddress: address(0), // Replace with the actual proxy address + contractName: "", // e.g., "ContractV2.sol:ContractV2" + constructorData: new bytes(0), // Replace with the actual constructor data + initData: new bytes(0) // Replace with the actual initialization data }); - UpgradeUtils.executeUpgrade(params); vm.stopBroadcast(); } } diff --git a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol index e58dfe37..8646ac01 100644 --- a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol +++ b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol @@ -100,22 +100,13 @@ contract Configure is Script { contract Upgrade is Script { function run() external { - string memory chain = vm.envString("CHAIN"); - ConfigLib.CommonConfigParams memory commonParams = ConfigLib.readCommonConfig(chain); - - // For testing purpose - uint256 newStateVariable = 1000000 * 10 ** 9; - address bridgeableToken = commonParams.approvalRequired - ? commonParams.rlcLiquidityUnifierAddress - : commonParams.rlcCrosschainTokenAddress; vm.startBroadcast(); - UpgradeUtils.UpgradeParams memory params = UpgradeUtils.UpgradeParams({ - proxyAddress: commonParams.iexecLayerZeroBridgeAddress, - constructorData: abi.encode(commonParams.approvalRequired, bridgeableToken, commonParams.lzEndpoint), - contractName: "IexecLayerZeroBridgeV2Mock.sol:IexecLayerZeroBridgeV2", // Would be production contract in real deployment - newStateVariable: newStateVariable + UpgradeUtils.executeUpgrade({ + proxyAddress: address(0), // Replace with the actual proxy address + contractName: "", // e.g., "ContractV2.sol:ContractV2" + constructorData: new bytes(0), // Replace with the actual constructor data + initData: new bytes(0) // Replace with the actual initialization data }); - UpgradeUtils.executeUpgrade(params); vm.stopBroadcast(); } } diff --git a/script/lib/UpgradeUtils.sol b/script/lib/UpgradeUtils.sol index 2eca2edc..bde7c568 100644 --- a/script/lib/UpgradeUtils.sol +++ b/script/lib/UpgradeUtils.sol @@ -3,50 +3,30 @@ pragma solidity ^0.8.22; import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {console} from "forge-std/console.sol"; /** * @title UpgradeUtils * @notice Utility library for handling UUPS upgrades in a parameterized way */ library UpgradeUtils { - struct UpgradeParams { - address proxyAddress; - string contractName; - bytes constructorData; - uint256 newStateVariable; // For initialization - } - - // Event for upgrade tracking - event UpgradeExecuted(string contractName, address indexed proxy, address indexed newImplementation); - - /** - * @notice Executes an Adapter upgrade with V2 initialization - * @param params Upgrade parameters (rlcToken field is required for ADAPTER) - * @return newImplementationAddress Address of the new implementation - */ - function executeUpgrade(UpgradeParams memory params) internal returns (address) { - Options memory opts = _buildOptions(params); - bytes memory initData = abi.encodeWithSignature("initializeV2(uint256)", params.newStateVariable); - - Upgrades.upgradeProxy(params.proxyAddress, params.contractName, initData, opts); - address newImplementation = Upgrades.getImplementationAddress(params.proxyAddress); - emit UpgradeExecuted(params.contractName, params.proxyAddress, newImplementation); - - return newImplementation; - } - - /** - * @notice Builds Options struct for upgrades - * @param params Upgrade parameters - * @return opts Configured Options struct - */ - function _buildOptions(UpgradeParams memory params) private pure returns (Options memory opts) { - opts.constructorData = params.constructorData; - // Ignore check related to LayerZero contracts: + function executeUpgrade( + address proxyAddress, + string memory contractName, + bytes memory constructorData, + bytes memory initData + ) internal returns (address newImplementation) { + Options memory opts; + opts.constructorData = constructorData; + // Ignore checks related to LayerZero contracts: // - OAppSenderUpgradeable // - OAppReceiverUpgradeable // - OFTCoreUpgradeable // - OAppCoreUpgradeable opts.unsafeAllow = "constructor,state-variable-immutable,missing-initializer-call"; + Upgrades.upgradeProxy(proxyAddress, contractName, initData, opts); + newImplementation = Upgrades.getImplementationAddress(proxyAddress); + console.log("Upgraded", contractName, " proxy to new implementation", newImplementation); + return newImplementation; } } diff --git a/test/units/RLCLiquidityUnifierUpgrade.t.sol b/test/units/RLCLiquidityUnifierUpgrade.t.sol index 20fba3db..78ece1cf 100644 --- a/test/units/RLCLiquidityUnifierUpgrade.t.sol +++ b/test/units/RLCLiquidityUnifierUpgrade.t.sol @@ -57,16 +57,12 @@ contract RLCLiquidityUnifierUpgradeTest is TestHelperOz5 { // 3. Perform upgrade using UpgradeUtils directly vm.startPrank(upgrader); - - UpgradeUtils.UpgradeParams memory params = UpgradeUtils.UpgradeParams({ + UpgradeUtils.executeUpgrade({ proxyAddress: proxyAddress, + contractName: "RLCLiquidityUnifierV2Mock.sol:RLCLiquidityUnifierV2", constructorData: abi.encode(rlcToken), - contractName: "RLCLiquidityUnifierV2Mock.sol:RLCLiquidityUnifierV2", - newStateVariable: NEW_STATE_VARIABLE + initData: abi.encodeWithSignature("initializeV2(uint256)", NEW_STATE_VARIABLE) }); - - UpgradeUtils.executeUpgrade(params); - vm.stopPrank(); rlcLiquidityUnifierV2 = RLCLiquidityUnifierV2(proxyAddress); diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol index b0fe7d43..36244b4a 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol @@ -53,15 +53,12 @@ contract IexecLayerZeroBridgeUpgradeTest is TestHelperOz5 { // 3. Perform upgrade using UpgradeUtils directly vm.startPrank(upgrader); - UpgradeUtils.UpgradeParams memory params = UpgradeUtils.UpgradeParams({ + UpgradeUtils.executeUpgrade({ proxyAddress: proxyAddress, + contractName: "IexecLayerZeroBridgeV2Mock.sol:IexecLayerZeroBridgeV2", constructorData: abi.encode(false, rlcCrosschainToken, mockEndpoint), - contractName: "IexecLayerZeroBridgeV2Mock.sol:IexecLayerZeroBridgeV2", - newStateVariable: NEW_STATE_VARIABLE + initData: abi.encodeWithSignature("initializeV2(uint256)", NEW_STATE_VARIABLE) }); - - UpgradeUtils.executeUpgrade(params); - vm.stopPrank(); iexecLayerZeroBridgeV2 = IexecLayerZeroBridgeV2(proxyAddress); From 3539588bad3371b2ec70b0584281d8d02921ad49 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:44:47 +0200 Subject: [PATCH 07/10] refactor: Rename upgrade function --- script/RLCCrosschainToken.s.sol | 2 +- script/RLCLiquidityUnifier.s.sol | 2 +- script/bridges/layerZero/IexecLayerZeroBridge.s.sol | 2 +- script/lib/UpgradeUtils.sol | 2 +- test/units/RLCLiquidityUnifierUpgrade.t.sol | 2 +- test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 3c6f773e..5e44af43 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -66,7 +66,7 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UpgradeUtils.executeUpgrade({ + UpgradeUtils.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/RLCLiquidityUnifier.s.sol b/script/RLCLiquidityUnifier.s.sol index 99db4ede..8cb13530 100644 --- a/script/RLCLiquidityUnifier.s.sol +++ b/script/RLCLiquidityUnifier.s.sol @@ -66,7 +66,7 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UpgradeUtils.executeUpgrade({ + UpgradeUtils.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol index 8646ac01..d724a5cd 100644 --- a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol +++ b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol @@ -101,7 +101,7 @@ contract Configure is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UpgradeUtils.executeUpgrade({ + UpgradeUtils.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/lib/UpgradeUtils.sol b/script/lib/UpgradeUtils.sol index bde7c568..606abb40 100644 --- a/script/lib/UpgradeUtils.sol +++ b/script/lib/UpgradeUtils.sol @@ -10,7 +10,7 @@ import {console} from "forge-std/console.sol"; * @notice Utility library for handling UUPS upgrades in a parameterized way */ library UpgradeUtils { - function executeUpgrade( + function upgrade( address proxyAddress, string memory contractName, bytes memory constructorData, diff --git a/test/units/RLCLiquidityUnifierUpgrade.t.sol b/test/units/RLCLiquidityUnifierUpgrade.t.sol index 78ece1cf..5bec3e74 100644 --- a/test/units/RLCLiquidityUnifierUpgrade.t.sol +++ b/test/units/RLCLiquidityUnifierUpgrade.t.sol @@ -57,7 +57,7 @@ contract RLCLiquidityUnifierUpgradeTest is TestHelperOz5 { // 3. Perform upgrade using UpgradeUtils directly vm.startPrank(upgrader); - UpgradeUtils.executeUpgrade({ + UpgradeUtils.upgrade({ proxyAddress: proxyAddress, contractName: "RLCLiquidityUnifierV2Mock.sol:RLCLiquidityUnifierV2", constructorData: abi.encode(rlcToken), diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol index 36244b4a..6309fcfa 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol @@ -53,7 +53,7 @@ contract IexecLayerZeroBridgeUpgradeTest is TestHelperOz5 { // 3. Perform upgrade using UpgradeUtils directly vm.startPrank(upgrader); - UpgradeUtils.executeUpgrade({ + UpgradeUtils.upgrade({ proxyAddress: proxyAddress, contractName: "IexecLayerZeroBridgeV2Mock.sol:IexecLayerZeroBridgeV2", constructorData: abi.encode(false, rlcCrosschainToken, mockEndpoint), From 5d00350024a6451e2e7188f91dfcc2e1a9d4692b Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:56:49 +0200 Subject: [PATCH 08/10] refactor: Merge upgrade and eploy scripts --- script/RLCCrosschainToken.s.sol | 3 +- script/RLCLiquidityUnifier.s.sol | 3 +- .../layerZero/IexecLayerZeroBridge.s.sol | 3 +- script/lib/UUPSProxyDeployer.sol | 29 +++++++++++++++++ script/lib/UpgradeUtils.sol | 32 ------------------- test/units/RLCLiquidityUnifierUpgrade.t.sol | 6 ++-- .../IexecLayerZeroBridgeUpgrade.t.sol | 6 ++-- 7 files changed, 38 insertions(+), 44 deletions(-) delete mode 100644 script/lib/UpgradeUtils.sol diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 5e44af43..46dbc3e3 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -8,7 +8,6 @@ import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; import {UUPSProxyDeployer} from "./lib/UUPSProxyDeployer.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; -import {UpgradeUtils} from "./lib/UpgradeUtils.sol"; /** * Deployment script for the RLCCrosschainToken contract. @@ -66,7 +65,7 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UpgradeUtils.upgrade({ + UUPSProxyDeployer.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/RLCLiquidityUnifier.s.sol b/script/RLCLiquidityUnifier.s.sol index 8cb13530..32ce8a42 100644 --- a/script/RLCLiquidityUnifier.s.sol +++ b/script/RLCLiquidityUnifier.s.sol @@ -8,7 +8,6 @@ import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {RLCLiquidityUnifier} from "../src/RLCLiquidityUnifier.sol"; import {UUPSProxyDeployer} from "./lib/UUPSProxyDeployer.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; -import {UpgradeUtils} from "./lib/UpgradeUtils.sol"; /** * Deployment script for the RLCLiquidityUnifier contract. * It reads configuration from a JSON file and deploys the contract using CreateX. @@ -66,7 +65,7 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UpgradeUtils.upgrade({ + UUPSProxyDeployer.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol index d724a5cd..d7e01634 100644 --- a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol +++ b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol @@ -11,7 +11,6 @@ import {IexecLayerZeroBridge} from "../../../src/bridges/layerZero/IexecLayerZer import {RLCLiquidityUnifier} from "../../../src/RLCLiquidityUnifier.sol"; import {RLCCrosschainToken} from "../../../src/RLCCrosschainToken.sol"; import {UUPSProxyDeployer} from "../../lib/UUPSProxyDeployer.sol"; -import {UpgradeUtils} from "../../lib/UpgradeUtils.sol"; contract Deploy is Script { /** @@ -101,7 +100,7 @@ contract Configure is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UpgradeUtils.upgrade({ + UUPSProxyDeployer.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index 9dfeb5b8..73e6abaf 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -7,6 +7,7 @@ import {Vm} from "forge-std/Vm.sol"; import {StdConstants} from "forge-std/StdConstants.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {ICreateX} from "@createx/contracts/ICreateX.sol"; +import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; /** * @notice Utility library for deploying UUPS proxy contracts and their implementations @@ -64,4 +65,32 @@ library UUPSProxyDeployer { console.log("Implementation deployed at:", implementation); return implementation; } + + /** + * Upgrades a UUPS proxy contract to a new implementation. + * @param proxyAddress address of the UUPS proxy contract to upgrade + * @param contractName name of the contract to upgrade (used to fetch creation code) + * @param constructorData constructor arguments for the new implementation contract + * @param initData initialization data for the proxy contract after upgrade + * @return newImplementation address of the new implementation contract + */ + function upgrade( + address proxyAddress, + string memory contractName, + bytes memory constructorData, + bytes memory initData + ) internal returns (address newImplementation) { + Options memory opts; + opts.constructorData = constructorData; + // Ignore checks related to LayerZero contracts: + // - OAppSenderUpgradeable + // - OAppReceiverUpgradeable + // - OFTCoreUpgradeable + // - OAppCoreUpgradeable + opts.unsafeAllow = "constructor,state-variable-immutable,missing-initializer-call"; + Upgrades.upgradeProxy(proxyAddress, contractName, initData, opts); + newImplementation = Upgrades.getImplementationAddress(proxyAddress); + console.log("Upgraded", contractName, " proxy to new implementation", newImplementation); + return newImplementation; + } } diff --git a/script/lib/UpgradeUtils.sol b/script/lib/UpgradeUtils.sol deleted file mode 100644 index 606abb40..00000000 --- a/script/lib/UpgradeUtils.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2025 IEXEC BLOCKCHAIN TECH -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.22; - -import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import {console} from "forge-std/console.sol"; - -/** - * @title UpgradeUtils - * @notice Utility library for handling UUPS upgrades in a parameterized way - */ -library UpgradeUtils { - function upgrade( - address proxyAddress, - string memory contractName, - bytes memory constructorData, - bytes memory initData - ) internal returns (address newImplementation) { - Options memory opts; - opts.constructorData = constructorData; - // Ignore checks related to LayerZero contracts: - // - OAppSenderUpgradeable - // - OAppReceiverUpgradeable - // - OFTCoreUpgradeable - // - OAppCoreUpgradeable - opts.unsafeAllow = "constructor,state-variable-immutable,missing-initializer-call"; - Upgrades.upgradeProxy(proxyAddress, contractName, initData, opts); - newImplementation = Upgrades.getImplementationAddress(proxyAddress); - console.log("Upgraded", contractName, " proxy to new implementation", newImplementation); - return newImplementation; - } -} diff --git a/test/units/RLCLiquidityUnifierUpgrade.t.sol b/test/units/RLCLiquidityUnifierUpgrade.t.sol index 5bec3e74..ac636932 100644 --- a/test/units/RLCLiquidityUnifierUpgrade.t.sol +++ b/test/units/RLCLiquidityUnifierUpgrade.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.22; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; import {RLCLiquidityUnifier} from "../../src/RLCLiquidityUnifier.sol"; import {TestUtils} from "./utils/TestUtils.sol"; -import {UpgradeUtils} from "../../script/lib/UpgradeUtils.sol"; +import {UUPSProxyDeployer} from "../../script/lib/UUPSProxyDeployer.sol"; import {RLCMock} from "./mocks/RLCMock.sol"; import {RLCLiquidityUnifierV2} from "./mocks/RLCLiquidityUnifierV2Mock.sol"; @@ -55,9 +55,9 @@ contract RLCLiquidityUnifierUpgradeTest is TestHelperOz5 { assertTrue(rlcLiquidityUnifierV1.hasRole(rlcLiquidityUnifierV1.DEFAULT_ADMIN_ROLE(), admin)); assertTrue(rlcLiquidityUnifierV1.hasRole(rlcLiquidityUnifierV1.UPGRADER_ROLE(), upgrader)); - // 3. Perform upgrade using UpgradeUtils directly + // 3. Perform upgrade vm.startPrank(upgrader); - UpgradeUtils.upgrade({ + UUPSProxyDeployer.upgrade({ proxyAddress: proxyAddress, contractName: "RLCLiquidityUnifierV2Mock.sol:RLCLiquidityUnifierV2", constructorData: abi.encode(rlcToken), diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol index 6309fcfa..f2bde490 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.22; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; import {IexecLayerZeroBridgeV2} from "../../mocks/IexecLayerZeroBridgeV2Mock.sol"; import {TestUtils} from "./../../utils/TestUtils.sol"; -import {UpgradeUtils} from "../../../../script/lib/UpgradeUtils.sol"; +import {UUPSProxyDeployer} from "../../../../script/lib/UUPSProxyDeployer.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; import {RLCCrosschainToken} from "../../../../src/RLCCrosschainToken.sol"; @@ -51,9 +51,9 @@ contract IexecLayerZeroBridgeUpgradeTest is TestHelperOz5 { assertTrue(iexecLayerZeroBridgeV1.hasRole(iexecLayerZeroBridgeV1.UPGRADER_ROLE(), upgrader)); assertTrue(iexecLayerZeroBridgeV1.hasRole(iexecLayerZeroBridgeV1.PAUSER_ROLE(), pauser)); - // 3. Perform upgrade using UpgradeUtils directly + // 3. Perform upgrade vm.startPrank(upgrader); - UpgradeUtils.upgrade({ + UUPSProxyDeployer.upgrade({ proxyAddress: proxyAddress, contractName: "IexecLayerZeroBridgeV2Mock.sol:IexecLayerZeroBridgeV2", constructorData: abi.encode(false, rlcCrosschainToken, mockEndpoint), From 7e69886eab836c16d38429dd348e25cc9d9898e7 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Tue, 8 Jul 2025 16:03:14 +0200 Subject: [PATCH 09/10] refactor: Rename utility contract --- script/RLCCrosschainToken.s.sol | 6 +- script/RLCLiquidityUnifier.s.sol | 6 +- .../layerZero/IexecLayerZeroBridge.s.sol | 6 +- script/lib/UUPSProxyDeployer.sol | 2 +- script/lib/UUPSProxyUtils.sol | 96 +++++++++++++++++++ test/units/RLCLiquidityUnifierUpgrade.t.sol | 4 +- .../IexecLayerZeroBridgeUpgrade.t.sol | 4 +- test/units/utils/TestUtils.sol | 8 +- 8 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 script/lib/UUPSProxyUtils.sol diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 46dbc3e3..24b91267 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; -import {UUPSProxyDeployer} from "./lib/UUPSProxyDeployer.sol"; +import {UUPSProxyUtils} from "./lib/UUPSProxyUtils.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; /** @@ -58,14 +58,14 @@ contract Deploy is Script { ) public returns (address) { bytes memory initData = abi.encodeWithSelector(RLCCrosschainToken.initialize.selector, name, symbol, initialAdmin, initialUpgrader); - return UUPSProxyDeployer.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); + return UUPSProxyUtils.deployUsingCreateX("RLCCrosschainToken", "", initData, createxFactory, createxSalt); } } contract Upgrade is Script { function run() external { vm.startBroadcast(); - UUPSProxyDeployer.upgrade({ + UUPSProxyUtils.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/RLCLiquidityUnifier.s.sol b/script/RLCLiquidityUnifier.s.sol index 32ce8a42..cf75e8a1 100644 --- a/script/RLCLiquidityUnifier.s.sol +++ b/script/RLCLiquidityUnifier.s.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {RLCLiquidityUnifier} from "../src/RLCLiquidityUnifier.sol"; -import {UUPSProxyDeployer} from "./lib/UUPSProxyDeployer.sol"; +import {UUPSProxyUtils} from "./lib/UUPSProxyUtils.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; /** * Deployment script for the RLCLiquidityUnifier contract. @@ -56,7 +56,7 @@ contract Deploy is Script { bytes memory constructorData = abi.encode(rlcToken); bytes memory initData = abi.encodeWithSelector(RLCLiquidityUnifier.initialize.selector, initialAdmin, initialUpgrader); - return UUPSProxyDeployer.deployUsingCreateX( + return UUPSProxyUtils.deployUsingCreateX( "RLCLiquidityUnifier", constructorData, initData, createxFactory, createxSalt ); } @@ -65,7 +65,7 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UUPSProxyDeployer.upgrade({ + UUPSProxyUtils.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol index d7e01634..03a1f889 100644 --- a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol +++ b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol @@ -10,7 +10,7 @@ import {ConfigLib} from "./../../lib/ConfigLib.sol"; import {IexecLayerZeroBridge} from "../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; import {RLCLiquidityUnifier} from "../../../src/RLCLiquidityUnifier.sol"; import {RLCCrosschainToken} from "../../../src/RLCCrosschainToken.sol"; -import {UUPSProxyDeployer} from "../../lib/UUPSProxyDeployer.sol"; +import {UUPSProxyUtils} from "../../lib/UUPSProxyUtils.sol"; contract Deploy is Script { /** @@ -52,7 +52,7 @@ contract Deploy is Script { bytes memory initializeData = abi.encodeWithSelector( IexecLayerZeroBridge.initialize.selector, initialAdmin, initialUpgrader, initialPauser ); - return UUPSProxyDeployer.deployUsingCreateX( + return UUPSProxyUtils.deployUsingCreateX( "IexecLayerZeroBridge", constructorData, initializeData, createxFactory, createxSalt ); } @@ -100,7 +100,7 @@ contract Configure is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UUPSProxyDeployer.upgrade({ + UUPSProxyUtils.upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol index 73e6abaf..5c2d5ade 100644 --- a/script/lib/UUPSProxyDeployer.sol +++ b/script/lib/UUPSProxyDeployer.sol @@ -13,7 +13,7 @@ import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; * @notice Utility library for deploying UUPS proxy contracts and their implementations * using the CreateX Factory. */ -library UUPSProxyDeployer { +library UUPSProxyUtils { /// @dev Reference to the VM cheat codes from forge-std Vm private constant vm = StdConstants.VM; diff --git a/script/lib/UUPSProxyUtils.sol b/script/lib/UUPSProxyUtils.sol new file mode 100644 index 00000000..5c2d5ade --- /dev/null +++ b/script/lib/UUPSProxyUtils.sol @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2025 IEXEC BLOCKCHAIN TECH +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.22; + +import {console} from "forge-std/console.sol"; +import {Vm} from "forge-std/Vm.sol"; +import {StdConstants} from "forge-std/StdConstants.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ICreateX} from "@createx/contracts/ICreateX.sol"; +import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; + +/** + * @notice Utility library for deploying UUPS proxy contracts and their implementations + * using the CreateX Factory. + */ +library UUPSProxyUtils { + /// @dev Reference to the VM cheat codes from forge-std + Vm private constant vm = StdConstants.VM; + + /** + * Deploys a UUPS proxy contract and its implementation in create2 mode using CreateX Factory. + * @param contractName The name of the contract to deploy (used to fetch creation code) + * @param constructorData The constructor arguments for the implementation contract + * @param initializeData The initialization data for the proxy contract + * @param createxFactory The address of the CreateX factory + * @param createxSalt The salt for deterministic deployment + * @return The address of the deployed proxy + */ + function deployUsingCreateX( + string memory contractName, + bytes memory constructorData, + bytes memory initializeData, + address createxFactory, + bytes32 createxSalt + ) internal returns (address) { + address implementation = + deployImplementationUsingCreateX(contractName, constructorData, createxFactory, createxSalt); + address proxy = ICreateX(createxFactory).deployCreate2AndInit( + createxSalt, + abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode + initializeData, + ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX + ); + console.log("UUPS Proxy deployed at:", proxy); + return proxy; + } + + /** + * Deploys the implementation contract in create2 mode using CreateX factory. + * @param contractName The name of the contract to deploy (used to fetch creation code) + * @param constructorData The constructor arguments for the implementation contract + * @param createxFactory The address of the CreateX factory + * @param createxSalt The salt for deterministic deployment + * @return The address of the deployed implementation contract + */ + function deployImplementationUsingCreateX( + string memory contractName, + bytes memory constructorData, + address createxFactory, + bytes32 createxSalt + ) internal returns (address) { + bytes memory creationCode = vm.getCode(contractName); + address implementation = + ICreateX(createxFactory).deployCreate2(createxSalt, abi.encodePacked(creationCode, constructorData)); + console.log("Implementation deployed at:", implementation); + return implementation; + } + + /** + * Upgrades a UUPS proxy contract to a new implementation. + * @param proxyAddress address of the UUPS proxy contract to upgrade + * @param contractName name of the contract to upgrade (used to fetch creation code) + * @param constructorData constructor arguments for the new implementation contract + * @param initData initialization data for the proxy contract after upgrade + * @return newImplementation address of the new implementation contract + */ + function upgrade( + address proxyAddress, + string memory contractName, + bytes memory constructorData, + bytes memory initData + ) internal returns (address newImplementation) { + Options memory opts; + opts.constructorData = constructorData; + // Ignore checks related to LayerZero contracts: + // - OAppSenderUpgradeable + // - OAppReceiverUpgradeable + // - OFTCoreUpgradeable + // - OAppCoreUpgradeable + opts.unsafeAllow = "constructor,state-variable-immutable,missing-initializer-call"; + Upgrades.upgradeProxy(proxyAddress, contractName, initData, opts); + newImplementation = Upgrades.getImplementationAddress(proxyAddress); + console.log("Upgraded", contractName, " proxy to new implementation", newImplementation); + return newImplementation; + } +} diff --git a/test/units/RLCLiquidityUnifierUpgrade.t.sol b/test/units/RLCLiquidityUnifierUpgrade.t.sol index ac636932..dc98a717 100644 --- a/test/units/RLCLiquidityUnifierUpgrade.t.sol +++ b/test/units/RLCLiquidityUnifierUpgrade.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.22; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; import {RLCLiquidityUnifier} from "../../src/RLCLiquidityUnifier.sol"; import {TestUtils} from "./utils/TestUtils.sol"; -import {UUPSProxyDeployer} from "../../script/lib/UUPSProxyDeployer.sol"; +import {UUPSProxyUtils} from "../../script/lib/UUPSProxyUtils.sol"; import {RLCMock} from "./mocks/RLCMock.sol"; import {RLCLiquidityUnifierV2} from "./mocks/RLCLiquidityUnifierV2Mock.sol"; @@ -57,7 +57,7 @@ contract RLCLiquidityUnifierUpgradeTest is TestHelperOz5 { // 3. Perform upgrade vm.startPrank(upgrader); - UUPSProxyDeployer.upgrade({ + UUPSProxyUtils.upgrade({ proxyAddress: proxyAddress, contractName: "RLCLiquidityUnifierV2Mock.sol:RLCLiquidityUnifierV2", constructorData: abi.encode(rlcToken), diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol index f2bde490..16e0b316 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.22; import {TestHelperOz5} from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; import {IexecLayerZeroBridgeV2} from "../../mocks/IexecLayerZeroBridgeV2Mock.sol"; import {TestUtils} from "./../../utils/TestUtils.sol"; -import {UUPSProxyDeployer} from "../../../../script/lib/UUPSProxyDeployer.sol"; +import {UUPSProxyUtils} from "../../../../script/lib/UUPSProxyUtils.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; import {RLCCrosschainToken} from "../../../../src/RLCCrosschainToken.sol"; @@ -53,7 +53,7 @@ contract IexecLayerZeroBridgeUpgradeTest is TestHelperOz5 { // 3. Perform upgrade vm.startPrank(upgrader); - UUPSProxyDeployer.upgrade({ + UUPSProxyUtils.upgrade({ proxyAddress: proxyAddress, contractName: "IexecLayerZeroBridgeV2Mock.sol:IexecLayerZeroBridgeV2", constructorData: abi.encode(false, rlcCrosschainToken, mockEndpoint), diff --git a/test/units/utils/TestUtils.sol b/test/units/utils/TestUtils.sol index 88671f8b..ddda46e1 100644 --- a/test/units/utils/TestUtils.sol +++ b/test/units/utils/TestUtils.sol @@ -6,7 +6,7 @@ import {CreateX} from "@createx/contracts/CreateX.sol"; import {OptionsBuilder} from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; import {MessagingFee, SendParam} from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; import {IOFT} from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; -import {UUPSProxyDeployer} from "../../../script/lib/UUPSProxyDeployer.sol"; +import {UUPSProxyUtils} from "../../../script/lib/UUPSProxyUtils.sol"; import {RLCMock} from "../mocks/RLCMock.sol"; import {IexecLayerZeroBridge} from "../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; import {RLCLiquidityUnifier} from "../../../src/RLCLiquidityUnifier.sol"; @@ -45,7 +45,7 @@ library TestUtils { // Deploy Liquidity Unifier rlcLiquidityUnifier = RLCLiquidityUnifier( - UUPSProxyDeployer.deployUsingCreateX( + UUPSProxyUtils.deployUsingCreateX( "RLCLiquidityUnifier", abi.encode(rlcToken), abi.encodeWithSelector(RLCLiquidityUnifier.initialize.selector, initialAdmin, initialUpgrader), @@ -56,7 +56,7 @@ library TestUtils { // Deploy IexecLayerZeroBridgeAdapter iexecLayerZeroBridgeChainA = IexecLayerZeroBridge( - UUPSProxyDeployer.deployUsingCreateX( + UUPSProxyUtils.deployUsingCreateX( "IexecLayerZeroBridge", abi.encode(true, rlcLiquidityUnifier, lzEndpointSource), abi.encodeWithSelector( @@ -75,7 +75,7 @@ library TestUtils { ); // Deploy IexecLayerZeroBridge iexecLayerZeroBridgeChainB = IexecLayerZeroBridge( - UUPSProxyDeployer.deployUsingCreateX( + UUPSProxyUtils.deployUsingCreateX( "IexecLayerZeroBridge", abi.encode(false, rlcCrosschainToken, lzEndpointDestination), abi.encodeWithSelector( From 8246a11cde3afaf4575c45cad832fd99e071e965 Mon Sep 17 00:00:00 2001 From: Zied <26070035+zguesmi@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:51:13 +0200 Subject: [PATCH 10/10] wip --- script/RLCCrosschainToken.s.sol | 16 +++- script/RLCLiquidityUnifier.s.sol | 15 ++- .../layerZero/IexecLayerZeroBridge.s.sol | 21 +++- script/lib/UUPSProxyDeployer.sol | 96 ------------------- script/lib/UUPSProxyUtils.sol | 18 +--- test/units/RLCLiquidityUnifierUpgrade.t.sol | 4 +- .../IexecLayerZeroBridgeUpgrade.t.sol | 3 +- 7 files changed, 56 insertions(+), 117 deletions(-) delete mode 100644 script/lib/UUPSProxyDeployer.sol diff --git a/script/RLCCrosschainToken.s.sol b/script/RLCCrosschainToken.s.sol index 24b91267..505c4aa0 100644 --- a/script/RLCCrosschainToken.s.sol +++ b/script/RLCCrosschainToken.s.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; -import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {RLCCrosschainToken} from "../src/RLCCrosschainToken.sol"; import {UUPSProxyUtils} from "./lib/UUPSProxyUtils.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; @@ -65,7 +65,7 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UUPSProxyUtils.upgrade({ + upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data @@ -73,4 +73,16 @@ contract Upgrade is Script { }); vm.stopBroadcast(); } + + // TODO add tests in `RLCCrosschainTokenUpgrade.t.sol`. + function upgrade( + address proxyAddress, + string memory contractName, + bytes memory constructorData, + bytes memory initData + ) public { + Options memory opts; + opts.constructorData = constructorData; + UUPSProxyUtils.executeUpgrade(proxyAddress, contractName, initData, opts); + } } diff --git a/script/RLCLiquidityUnifier.s.sol b/script/RLCLiquidityUnifier.s.sol index cf75e8a1..8ac11d33 100644 --- a/script/RLCLiquidityUnifier.s.sol +++ b/script/RLCLiquidityUnifier.s.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; -import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {RLCLiquidityUnifier} from "../src/RLCLiquidityUnifier.sol"; import {UUPSProxyUtils} from "./lib/UUPSProxyUtils.sol"; import {ConfigLib} from "./lib/ConfigLib.sol"; @@ -65,7 +65,7 @@ contract Deploy is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UUPSProxyUtils.upgrade({ + upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data @@ -73,4 +73,15 @@ contract Upgrade is Script { }); vm.stopBroadcast(); } + + function upgrade( + address proxyAddress, + string memory contractName, + bytes memory constructorData, + bytes memory initData + ) public { + Options memory opts; + opts.constructorData = constructorData; + UUPSProxyUtils.executeUpgrade(proxyAddress, contractName, initData, opts); + } } diff --git a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol index 03a1f889..52f1774d 100644 --- a/script/bridges/layerZero/IexecLayerZeroBridge.s.sol +++ b/script/bridges/layerZero/IexecLayerZeroBridge.s.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.22; import {Script} from "forge-std/Script.sol"; -import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {EnforcedOptionParam} from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; import {OptionsBuilder} from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; import {ConfigLib} from "./../../lib/ConfigLib.sol"; @@ -100,7 +100,7 @@ contract Configure is Script { contract Upgrade is Script { function run() external { vm.startBroadcast(); - UUPSProxyUtils.upgrade({ + upgrade({ proxyAddress: address(0), // Replace with the actual proxy address contractName: "", // e.g., "ContractV2.sol:ContractV2" constructorData: new bytes(0), // Replace with the actual constructor data @@ -108,4 +108,21 @@ contract Upgrade is Script { }); vm.stopBroadcast(); } + + function upgrade( + address proxyAddress, + string memory contractName, + bytes memory constructorData, + bytes memory initData + ) public { + Options memory opts; + opts.constructorData = constructorData; + // Ignore checks related to LayerZero contracts: + // - OAppSenderUpgradeable + // - OAppReceiverUpgradeable + // - OFTCoreUpgradeable + // - OAppCoreUpgradeable + opts.unsafeAllow = "constructor,state-variable-immutable,missing-initializer-call"; + UUPSProxyUtils.executeUpgrade(proxyAddress, contractName, initData, opts); + } } diff --git a/script/lib/UUPSProxyDeployer.sol b/script/lib/UUPSProxyDeployer.sol deleted file mode 100644 index 5c2d5ade..00000000 --- a/script/lib/UUPSProxyDeployer.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: 2025 IEXEC BLOCKCHAIN TECH -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.22; - -import {console} from "forge-std/console.sol"; -import {Vm} from "forge-std/Vm.sol"; -import {StdConstants} from "forge-std/StdConstants.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {ICreateX} from "@createx/contracts/ICreateX.sol"; -import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; - -/** - * @notice Utility library for deploying UUPS proxy contracts and their implementations - * using the CreateX Factory. - */ -library UUPSProxyUtils { - /// @dev Reference to the VM cheat codes from forge-std - Vm private constant vm = StdConstants.VM; - - /** - * Deploys a UUPS proxy contract and its implementation in create2 mode using CreateX Factory. - * @param contractName The name of the contract to deploy (used to fetch creation code) - * @param constructorData The constructor arguments for the implementation contract - * @param initializeData The initialization data for the proxy contract - * @param createxFactory The address of the CreateX factory - * @param createxSalt The salt for deterministic deployment - * @return The address of the deployed proxy - */ - function deployUsingCreateX( - string memory contractName, - bytes memory constructorData, - bytes memory initializeData, - address createxFactory, - bytes32 createxSalt - ) internal returns (address) { - address implementation = - deployImplementationUsingCreateX(contractName, constructorData, createxFactory, createxSalt); - address proxy = ICreateX(createxFactory).deployCreate2AndInit( - createxSalt, - abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(implementation, "")), // initCode - initializeData, - ICreateX.Values({constructorAmount: 0, initCallAmount: 0}) // values for CreateX - ); - console.log("UUPS Proxy deployed at:", proxy); - return proxy; - } - - /** - * Deploys the implementation contract in create2 mode using CreateX factory. - * @param contractName The name of the contract to deploy (used to fetch creation code) - * @param constructorData The constructor arguments for the implementation contract - * @param createxFactory The address of the CreateX factory - * @param createxSalt The salt for deterministic deployment - * @return The address of the deployed implementation contract - */ - function deployImplementationUsingCreateX( - string memory contractName, - bytes memory constructorData, - address createxFactory, - bytes32 createxSalt - ) internal returns (address) { - bytes memory creationCode = vm.getCode(contractName); - address implementation = - ICreateX(createxFactory).deployCreate2(createxSalt, abi.encodePacked(creationCode, constructorData)); - console.log("Implementation deployed at:", implementation); - return implementation; - } - - /** - * Upgrades a UUPS proxy contract to a new implementation. - * @param proxyAddress address of the UUPS proxy contract to upgrade - * @param contractName name of the contract to upgrade (used to fetch creation code) - * @param constructorData constructor arguments for the new implementation contract - * @param initData initialization data for the proxy contract after upgrade - * @return newImplementation address of the new implementation contract - */ - function upgrade( - address proxyAddress, - string memory contractName, - bytes memory constructorData, - bytes memory initData - ) internal returns (address newImplementation) { - Options memory opts; - opts.constructorData = constructorData; - // Ignore checks related to LayerZero contracts: - // - OAppSenderUpgradeable - // - OAppReceiverUpgradeable - // - OFTCoreUpgradeable - // - OAppCoreUpgradeable - opts.unsafeAllow = "constructor,state-variable-immutable,missing-initializer-call"; - Upgrades.upgradeProxy(proxyAddress, contractName, initData, opts); - newImplementation = Upgrades.getImplementationAddress(proxyAddress); - console.log("Upgraded", contractName, " proxy to new implementation", newImplementation); - return newImplementation; - } -} diff --git a/script/lib/UUPSProxyUtils.sol b/script/lib/UUPSProxyUtils.sol index 5c2d5ade..5c9d063b 100644 --- a/script/lib/UUPSProxyUtils.sol +++ b/script/lib/UUPSProxyUtils.sol @@ -69,25 +69,17 @@ library UUPSProxyUtils { /** * Upgrades a UUPS proxy contract to a new implementation. * @param proxyAddress address of the UUPS proxy contract to upgrade - * @param contractName name of the contract to upgrade (used to fetch creation code) - * @param constructorData constructor arguments for the new implementation contract + * @param contractName name of the contract to upgrade (e.g. "ContractV2.sol:ContractV2") + * @param opts options for the upgrade, such as unsafeAllow and others. * @param initData initialization data for the proxy contract after upgrade * @return newImplementation address of the new implementation contract */ - function upgrade( + function executeUpgrade( address proxyAddress, string memory contractName, - bytes memory constructorData, - bytes memory initData + bytes memory initData, + Options memory opts ) internal returns (address newImplementation) { - Options memory opts; - opts.constructorData = constructorData; - // Ignore checks related to LayerZero contracts: - // - OAppSenderUpgradeable - // - OAppReceiverUpgradeable - // - OFTCoreUpgradeable - // - OAppCoreUpgradeable - opts.unsafeAllow = "constructor,state-variable-immutable,missing-initializer-call"; Upgrades.upgradeProxy(proxyAddress, contractName, initData, opts); newImplementation = Upgrades.getImplementationAddress(proxyAddress); console.log("Upgraded", contractName, " proxy to new implementation", newImplementation); diff --git a/test/units/RLCLiquidityUnifierUpgrade.t.sol b/test/units/RLCLiquidityUnifierUpgrade.t.sol index dc98a717..056a847b 100644 --- a/test/units/RLCLiquidityUnifierUpgrade.t.sol +++ b/test/units/RLCLiquidityUnifierUpgrade.t.sol @@ -8,7 +8,9 @@ import {TestUtils} from "./utils/TestUtils.sol"; import {UUPSProxyUtils} from "../../script/lib/UUPSProxyUtils.sol"; import {RLCMock} from "./mocks/RLCMock.sol"; import {RLCLiquidityUnifierV2} from "./mocks/RLCLiquidityUnifierV2Mock.sol"; +import {Upgrade as RLCLiquidityUnifierUpgradeScript} from "../../script/RLCLiquidityUnifier.s.sol"; +// TODO remove useless LZ setup (endpoint, ...). contract RLCLiquidityUnifierUpgradeTest is TestHelperOz5 { using TestUtils for *; @@ -57,7 +59,7 @@ contract RLCLiquidityUnifierUpgradeTest is TestHelperOz5 { // 3. Perform upgrade vm.startPrank(upgrader); - UUPSProxyUtils.upgrade({ + new RLCLiquidityUnifierUpgradeScript().upgrade({ proxyAddress: proxyAddress, contractName: "RLCLiquidityUnifierV2Mock.sol:RLCLiquidityUnifierV2", constructorData: abi.encode(rlcToken), diff --git a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol index 16e0b316..216911e0 100644 --- a/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol +++ b/test/units/bridges/layerZero/IexecLayerZeroBridgeUpgrade.t.sol @@ -8,6 +8,7 @@ import {TestUtils} from "./../../utils/TestUtils.sol"; import {UUPSProxyUtils} from "../../../../script/lib/UUPSProxyUtils.sol"; import {IexecLayerZeroBridge} from "../../../../src/bridges/layerZero/IexecLayerZeroBridge.sol"; import {RLCCrosschainToken} from "../../../../src/RLCCrosschainToken.sol"; +import {Upgrade as IexecLayerZeroBridgeUpgradeScript} from "../../../../script/bridges/layerZero/IexecLayerZeroBridge.s.sol"; contract IexecLayerZeroBridgeUpgradeTest is TestHelperOz5 { using TestUtils for *; @@ -53,7 +54,7 @@ contract IexecLayerZeroBridgeUpgradeTest is TestHelperOz5 { // 3. Perform upgrade vm.startPrank(upgrader); - UUPSProxyUtils.upgrade({ + new IexecLayerZeroBridgeUpgradeScript().upgrade({ proxyAddress: proxyAddress, contractName: "IexecLayerZeroBridgeV2Mock.sol:IexecLayerZeroBridgeV2", constructorData: abi.encode(false, rlcCrosschainToken, mockEndpoint),