From cc0f5baa8825fdcb22a0913a08a692e85e1221ab Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 19 Feb 2024 16:14:14 -0500 Subject: [PATCH 1/9] feat: add proxy upgrade helper library --- test/utils/Greeter.sol | 11 ++ test/utils/GreeterProxiable.sol | 65 +++++++ test/utils/GreeterV2.sol | 14 ++ test/utils/GreeterV2Proxiable.sol | 17 ++ test/utils/NoInitializer.sol | 13 ++ test/utils/ProxyTestContracts.sol | 9 + test/utils/ProxyUtils.sol | 302 ++++++++++++++++++++++++++++++ test/utils/ProxyUtils.t.sol | 90 +++++++++ test/utils/WithConstructor.sol | 19 ++ 9 files changed, 540 insertions(+) create mode 100644 test/utils/Greeter.sol create mode 100644 test/utils/GreeterProxiable.sol create mode 100644 test/utils/GreeterV2.sol create mode 100644 test/utils/GreeterV2Proxiable.sol create mode 100644 test/utils/NoInitializer.sol create mode 100644 test/utils/ProxyTestContracts.sol create mode 100644 test/utils/ProxyUtils.sol create mode 100644 test/utils/ProxyUtils.t.sol create mode 100644 test/utils/WithConstructor.sol diff --git a/test/utils/Greeter.sol b/test/utils/Greeter.sol new file mode 100644 index 00000000..35948a85 --- /dev/null +++ b/test/utils/Greeter.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +contract Greeter { + string public greeting; + + function initialize(string memory _greeting) public { + greeting = _greeting; + } +} + diff --git a/test/utils/GreeterProxiable.sol b/test/utils/GreeterProxiable.sol new file mode 100644 index 00000000..e6a6e17c --- /dev/null +++ b/test/utils/GreeterProxiable.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +interface IERC1822Proxiable { + function proxiableUUID() external view returns (bytes32); +} + +contract Proxiable { + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; + + function upgradeToAndCall(address newImplementation, bytes calldata data) external { + try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { + if (slot != _IMPLEMENTATION_SLOT) { + revert("slot is unsupported as a uuid"); + } + _setImplementation(newImplementation); + if (data.length > 0) { + /** + * Note that using delegate call can make your implementation contract vulnerable if this function + * is not protected with the `onlyProxy` modifier. Again, this contract is for testing only, it is + * not safe for use in production. Instead, use the `UUPSUpgradeable` contract available in + * @openzeppelin/contracts-upgradeable + */ + /// @custom:oz-upgrades-unsafe-allow delegatecall + (bool success, ) = newImplementation.delegatecall(data); + require(success, "upgrade call reverted"); + } else { + _checkNonPayable(); + } + } catch { + revert("the implementation is not UUPS"); + } + } + + function proxiableUUID() external view virtual returns (bytes32) { + return _IMPLEMENTATION_SLOT; + } + + function _checkNonPayable() private { + if (msg.value > 0) { + revert("non-payable upgrade call"); + } + } + + function _setImplementation(address newImplementation) private { + bytes32 slot = _IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly + assembly { + sstore(slot, newImplementation) + } + } +} + + +contract GreeterProxiable is Proxiable { + string public greeting; + + function initialize(string memory _greeting) public { + greeting = _greeting; + } +} + + diff --git a/test/utils/GreeterV2.sol b/test/utils/GreeterV2.sol new file mode 100644 index 00000000..15a2b9b8 --- /dev/null +++ b/test/utils/GreeterV2.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +contract GreeterV2 { + string public greeting; + + function initialize(string memory _greeting) public { + greeting = _greeting; + } + + function resetGreeting() public { + greeting = "resetted"; + } +} diff --git a/test/utils/GreeterV2Proxiable.sol b/test/utils/GreeterV2Proxiable.sol new file mode 100644 index 00000000..ca2d82c9 --- /dev/null +++ b/test/utils/GreeterV2Proxiable.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {Proxiable} from "./GreeterProxiable.sol"; + +contract GreeterV2Proxiable is Proxiable { + string public greeting; + + function initialize(string memory _greeting) public { + greeting = _greeting; + } + + function resetGreeting() public { + greeting = "resetted"; + } +} + diff --git a/test/utils/NoInitializer.sol b/test/utils/NoInitializer.sol new file mode 100644 index 00000000..e70616ae --- /dev/null +++ b/test/utils/NoInitializer.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +contract NoInitializer { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + uint256 public immutable a; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(uint256 _a) { + a = _a; + } +} + diff --git a/test/utils/ProxyTestContracts.sol b/test/utils/ProxyTestContracts.sol new file mode 100644 index 00000000..af2bc907 --- /dev/null +++ b/test/utils/ProxyTestContracts.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {Greeter} from "./Greeter.sol"; +import {GreeterProxiable} from "./GreeterProxiable.sol"; +import {GreeterV2} from "./GreeterV2.sol"; +import {GreeterV2Proxiable} from "./GreeterV2Proxiable.sol"; +import {WithConstructor} from "./WithConstructor.sol"; +import {NoInitializer} from "./NoInitializer.sol"; diff --git a/test/utils/ProxyUtils.sol b/test/utils/ProxyUtils.sol new file mode 100644 index 00000000..a0f50451 --- /dev/null +++ b/test/utils/ProxyUtils.sol @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {Vm} from "forge-std/Vm.sol"; +import {ERC1967Proxy} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import {UpgradeableBeacon} from "openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol"; +import {TransparentUpgradeableProxy} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +/// Modified from the Openzeppelin foundry upgrades library +/// Modifications: +/// - Made compatible with OZ ^4.x releases +/// - Removed OZ Defender functionality +library UpgradeableProxyUtils { + address private constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; + + // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. + * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. + */ + bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + Vm private constant vm = Vm(CHEATCODE_ADDRESS); + + /// @dev Convenience function for pranking the admin in tests + modifier tryPrank(address _tryCaller) { + try vm.startPrank(_tryCaller) { + _; + vm.stopPrank(); + } catch { + _; + } + } + + /** + * @dev Deploys a transparent proxy using the given contract as the implementation. + * + * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy + * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + * @return Proxy address + */ + function deployTransparentProxy( + string memory contractName, + address initialOwner, + bytes memory initializerData + ) internal returns (address) { + return deployTransparentProxy(contractName, initialOwner, initializerData, ""); + } + + /** + * @dev Deploys a transparent proxy using the given contract as the implementation. + * + * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy + * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + * @return Proxy address + */ + function deployTransparentProxy( + string memory contractName, + address initialOwner, + bytes memory initializerData, + bytes memory constructorData + ) internal returns (address) { + address impl = deployImplementation(contractName, constructorData); + return + address( + _deploy( + "TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy", + abi.encode(impl, initialOwner, initializerData) + ) + ); + } + + /** + * @dev Deploys an upgradeable beacon using the given contract as the implementation. + * + * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param initialOwner Address to set as the owner of the UpgradeableBeacon contract which gets deployed + * @return Beacon address + */ + function deployBeacon( + string memory contractName, + address initialOwner, + bytes memory constructorData + ) internal returns (address) { + address impl = deployImplementation(contractName, constructorData); + return _deploy("UpgradeableBeacon.sol:UpgradeableBeacon", abi.encode(impl, initialOwner)); + } + + /** + * @dev Deploys a beacon proxy using the given beacon and call data. + * + * @param beacon Address of the beacon to use + * @param data Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required + * @return Proxy address + */ + function deployBeaconProxy(address beacon, bytes memory data) internal returns (address) { + return _deploy("BeaconProxy.sol:BeaconProxy", abi.encode(beacon, data)); + } + + /** + * @dev Validates and deploys an implementation contract, and returns its address. + * + * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @return Address of the implementation contract + */ + function deployImplementation(string memory contractName, bytes memory constructorData) internal returns (address) { + return _deploy(contractName, constructorData); + } + /** + * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. + * + * @param proxy Address of a transparent proxy + * @return Admin address + */ + function getAdminAddress(address proxy) internal view returns (address) { + bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); + return address(uint160(uint256(adminSlot))); + } + + /** + * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. + * + * @param proxy Address of a transparent or UUPS proxy + * @return Implementation address + */ + function getImplementationAddress(address proxy) internal view returns (address) { + bytes32 implSlot = vm.load(proxy, _IMPLEMENTATION_SLOT); + return address(uint160(uint256(implSlot))); + } + + /** + * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. + * + * @param proxy Address of a beacon proxy + * @return Beacon address + */ + function getBeaconAddress(address proxy) internal view returns (address) { + bytes32 beaconSlot = vm.load(proxy, _BEACON_SLOT); + return address(uint160(uint256(beaconSlot))); + } + + function _deploy( + string memory contractName, + bytes memory constructorData + ) private returns (address) { + bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); + address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); + if (deployedAddress == address(0)) { + revert( + string.concat( + "Failed to deploy contract ", + contractName, + ' using constructor data "', + string(constructorData), + '"' + ) + ); + } + return deployedAddress; + } + + function _deployFromBytecode(bytes memory bytecode) private returns (address) { + address addr; + assembly { + addr := create(0, add(bytecode, 32), mload(bytecode)) + } + return addr; + } + + /** + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param proxy Address of the proxy to upgrade + * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + */ + function upgradeProxy(address proxy, string memory contractName, bytes memory data, bytes memory constructorData) internal { + address newImpl = prepareUpgrade(contractName, constructorData); + + bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); + if (adminSlot == bytes32(0)) { + + // No admin contract: upgrade directly using interface + TransparentUpgradeableProxy(payable(proxy)).upgradeToAndCall(newImpl, data); + } else { + ProxyAdmin admin = ProxyAdmin(address(uint160(uint256(adminSlot)))); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), newImpl, data); + } + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param proxy Address of the proxy to upgrade + * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. + */ + function upgradeProxy( + address proxy, + string memory contractName, + bytes memory data, + bytes memory constructorData, + address tryCaller + ) internal tryPrank(tryCaller) { + upgradeProxy(proxy, contractName, data, constructorData); + } + + /** + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * @param beacon Address of the beacon to upgrade + * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + */ + function upgradeBeacon(address beacon, string memory contractName, bytes memory constructorData) internal { + address newImpl = prepareUpgrade(contractName, constructorData); + UpgradeableBeacon(beacon).upgradeTo(newImpl); + } + + /** + * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. + * + * @dev Upgrades a beacon to a new implementation contract. + * + * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. + * + * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. + * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. + * + * @param beacon Address of the beacon to upgrade + * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. + */ + function upgradeBeacon( + address beacon, + string memory contractName, + bytes memory constructorData, + address tryCaller + ) internal tryPrank(tryCaller) { + upgradeBeacon(beacon, contractName, constructorData); + } + + /** + * @dev Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, + * and returns its address. + * + * Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. + * + * Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from your deployment environment. + * + * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @return Address of the new implementation contract + */ + function prepareUpgrade(string memory contractName, bytes memory constructorData) internal returns (address) { + return _deploy(contractName, constructorData); + } + + /** + * @dev Precompile proxy contracts so that they can be deployed by name via the `_deploy` function. + * + * NOTE: This function is never called and has no effect, but must be kept to ensure that the proxy contracts are included in the compilation. + */ + function _precompileProxyContracts() private pure { + bytes memory dummy; + dummy = type(ERC1967Proxy).creationCode; + dummy = type(TransparentUpgradeableProxy).creationCode; + dummy = type(ProxyAdmin).creationCode; + dummy = type(UpgradeableBeacon).creationCode; + dummy = type(BeaconProxy).creationCode; + } +} diff --git a/test/utils/ProxyUtils.t.sol b/test/utils/ProxyUtils.t.sol new file mode 100644 index 00000000..bef0a91d --- /dev/null +++ b/test/utils/ProxyUtils.t.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {Test, console} from "forge-std/Test.sol"; +import {Vm} from "forge-std/Vm.sol"; + +import {UpgradeableProxyUtils} from "./ProxyUtils.sol"; +import {Proxy} from "openzeppelin-contracts/contracts/proxy/Proxy.sol"; +import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/Transparent/ProxyAdmin.sol"; +import {IBeacon} from "openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol"; +import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; + +contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { + address constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; + + function testTransparent() public { + address proxy = UpgradeableProxyUtils.deployTransparentProxy( + "Greeter.sol", + address(this), + abi.encodeCall(Greeter.initialize, ("hello")) + ); + Greeter instance = Greeter(proxy); + address implAddressV1 = UpgradeableProxyUtils.getImplementationAddress(proxy); + address adminAddress = UpgradeableProxyUtils.getAdminAddress(proxy); + + assertFalse(adminAddress == address(0)); + + vm.startPrank(address(420)); + assertEq(instance.greeting(), "hello"); + vm.stopPrank(); + + vm.startPrank(ProxyAdmin(address(this)).owner()); + UpgradeableProxyUtils.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ()), abi.encode(), address(this)); + vm.stopPrank(); + + address implAddressV2 = UpgradeableProxyUtils.getImplementationAddress(proxy); + + assertEq(UpgradeableProxyUtils.getAdminAddress(proxy), adminAddress); + + vm.startPrank(address(420)); + assertEq(instance.greeting(), "resetted"); + vm.stopPrank(); + assertFalse(implAddressV2 == implAddressV1); + } + + function testBeacon() public { + address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(this), abi.encode()); + address implAddressV1 = IBeacon(beacon).implementation(); + + address proxy = UpgradeableProxyUtils.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); + Greeter instance = Greeter(proxy); + + assertEq(UpgradeableProxyUtils.getBeaconAddress(proxy), beacon); + + assertEq(instance.greeting(), "hello"); + + UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol", abi.encode(), address(this)); + address implAddressV2 = IBeacon(beacon).implementation(); + + GreeterV2(address(instance)).resetGreeting(); + + assertEq(instance.greeting(), "resetted"); + assertFalse(implAddressV2 == implAddressV1); + } + + function testUpgradeBeaconWithoutCaller() public { + address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(this), abi.encode()); + UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol", abi.encode()); + } + + function testWithConstructor() public { + bytes memory constructorData = abi.encode(123); + address proxy = UpgradeableProxyUtils.deployTransparentProxy( + "WithConstructor.sol", + msg.sender, + abi.encodeCall(WithConstructor.initialize, (456)), + constructorData + ); + assertEq(WithConstructor(proxy).a(), 123); + assertEq(WithConstructor(proxy).b(), 456); + } + + function testNoInitializer() public { + /// Can access getCode by File:Contract + bytes memory constructorData = abi.encode(123); + address proxy = UpgradeableProxyUtils.deployTransparentProxy("NoInitializer.sol", msg.sender, "", constructorData); + assertEq(WithConstructor(proxy).a(), 123); + } + +} diff --git a/test/utils/WithConstructor.sol b/test/utils/WithConstructor.sol new file mode 100644 index 00000000..146b3ecd --- /dev/null +++ b/test/utils/WithConstructor.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +contract WithConstructor { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + uint256 public immutable a; + + uint256 public b; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(uint256 _a) { + a = _a; + } + + function initialize(uint256 _b) public { + b = _b; + } +} + From 3a0e1ff37a73b86a4a8f040fc323cd92816ecb8a Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 19 Feb 2024 16:40:40 -0500 Subject: [PATCH 2/9] chore: rename oz dep and file --- .../{ProxyUtils.sol => UpgradeableProxyUtils.sol} | 10 +++++----- .../{ProxyUtils.t.sol => UpgradeableProxyUtils.t.sol} | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) rename test/utils/{ProxyUtils.sol => UpgradeableProxyUtils.sol} (96%) rename test/utils/{ProxyUtils.t.sol => UpgradeableProxyUtils.t.sol} (91%) diff --git a/test/utils/ProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol similarity index 96% rename from test/utils/ProxyUtils.sol rename to test/utils/UpgradeableProxyUtils.sol index a0f50451..5f0d93e6 100644 --- a/test/utils/ProxyUtils.sol +++ b/test/utils/UpgradeableProxyUtils.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.12; import {Vm} from "forge-std/Vm.sol"; -import {ERC1967Proxy} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; -import {UpgradeableBeacon} from "openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import {BeaconProxy} from "openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol"; -import {TransparentUpgradeableProxy} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; /// Modified from the Openzeppelin foundry upgrades library /// Modifications: diff --git a/test/utils/ProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol similarity index 91% rename from test/utils/ProxyUtils.t.sol rename to test/utils/UpgradeableProxyUtils.t.sol index bef0a91d..7014b69e 100644 --- a/test/utils/ProxyUtils.t.sol +++ b/test/utils/UpgradeableProxyUtils.t.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.12; import {Test, console} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; -import {UpgradeableProxyUtils} from "./ProxyUtils.sol"; -import {Proxy} from "openzeppelin-contracts/contracts/proxy/Proxy.sol"; -import {ProxyAdmin} from "openzeppelin-contracts/contracts/proxy/Transparent/ProxyAdmin.sol"; -import {IBeacon} from "openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol"; +import {UpgradeableProxyUtils} from "./UpgradeableProxyUtils.sol"; +import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/Transparent/ProxyAdmin.sol"; +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { From caa64210730b8c2f3a70f8c0959c2714bccabce8 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 19 Feb 2024 16:43:13 -0500 Subject: [PATCH 3/9] fix: typo --- test/utils/UpgradeableProxyUtils.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/UpgradeableProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol index 7014b69e..15a6f0bc 100644 --- a/test/utils/UpgradeableProxyUtils.t.sol +++ b/test/utils/UpgradeableProxyUtils.t.sol @@ -6,7 +6,7 @@ import {Vm} from "forge-std/Vm.sol"; import {UpgradeableProxyUtils} from "./UpgradeableProxyUtils.sol"; import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/Transparent/ProxyAdmin.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; From e17d4400a0c7f6a7da7c8081b8e6e0afabbf465e Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 19 Feb 2024 17:23:33 -0500 Subject: [PATCH 4/9] chore: overload functions --- test/utils/UpgradeableProxyUtils.sol | 49 ++++---------------------- test/utils/UpgradeableProxyUtils.t.sol | 4 +-- 2 files changed, 8 insertions(+), 45 deletions(-) diff --git a/test/utils/UpgradeableProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol index 5f0d93e6..20ebafda 100644 --- a/test/utils/UpgradeableProxyUtils.sol +++ b/test/utils/UpgradeableProxyUtils.sol @@ -40,16 +40,6 @@ library UpgradeableProxyUtils { Vm private constant vm = Vm(CHEATCODE_ADDRESS); - /// @dev Convenience function for pranking the admin in tests - modifier tryPrank(address _tryCaller) { - try vm.startPrank(_tryCaller) { - _; - vm.stopPrank(); - } catch { - _; - } - } - /** * @dev Deploys a transparent proxy using the given contract as the implementation. * @@ -211,28 +201,16 @@ library UpgradeableProxyUtils { } /** - * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. - * * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. * * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. * - * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. - * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. - * * @param proxy Address of the proxy to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the proxy or its ProxyAdmin. */ - function upgradeProxy( - address proxy, - string memory contractName, - bytes memory data, - bytes memory constructorData, - address tryCaller - ) internal tryPrank(tryCaller) { - upgradeProxy(proxy, contractName, data, constructorData); + function upgradeProxy(address proxy, string memory contractName, bytes memory data) internal { + upgradeProxy(proxy, contractName, data, ""); } /** @@ -248,29 +226,14 @@ library UpgradeableProxyUtils { UpgradeableBeacon(beacon).upgradeTo(newImpl); } - /** - * @notice For tests only. If broadcasting in scripts, use the `--sender
` option with `forge script` instead. - * - * @dev Upgrades a beacon to a new implementation contract. - * - * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - * - * This function provides an additional `tryCaller` parameter to test an upgrade using a specific caller address. - * Use this if you encounter `OwnableUnauthorizedAccount` errors in your tests. - * + + /* * @param beacon Address of the beacon to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param tryCaller Address to use as the caller of the upgrade function. This should be the address that owns the beacon. */ - function upgradeBeacon( - address beacon, - string memory contractName, - bytes memory constructorData, - address tryCaller - ) internal tryPrank(tryCaller) { - upgradeBeacon(beacon, contractName, constructorData); + function upgradeBeacon(address beacon, string memory contractName ) internal { + upgradeBeacon(beacon, contractName, ""); } - /** * @dev Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, * and returns its address. diff --git a/test/utils/UpgradeableProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol index 15a6f0bc..234c1764 100644 --- a/test/utils/UpgradeableProxyUtils.t.sol +++ b/test/utils/UpgradeableProxyUtils.t.sol @@ -30,7 +30,7 @@ contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { vm.stopPrank(); vm.startPrank(ProxyAdmin(address(this)).owner()); - UpgradeableProxyUtils.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ()), abi.encode(), address(this)); + UpgradeableProxyUtils.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ())); vm.stopPrank(); address implAddressV2 = UpgradeableProxyUtils.getImplementationAddress(proxy); @@ -54,7 +54,7 @@ contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { assertEq(instance.greeting(), "hello"); - UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol", abi.encode(), address(this)); + UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol"); address implAddressV2 = IBeacon(beacon).implementation(); GreeterV2(address(instance)).resetGreeting(); From 79191e53d08b80242f39fcec914cee4c55ff317c Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 19 Feb 2024 17:35:47 -0500 Subject: [PATCH 5/9] chore: cleanup --- test/utils/UpgradeableProxyUtils.t.sol | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/test/utils/UpgradeableProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol index 234c1764..ed79a291 100644 --- a/test/utils/UpgradeableProxyUtils.t.sol +++ b/test/utils/UpgradeableProxyUtils.t.sol @@ -10,13 +10,18 @@ import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.s import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; -contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { - address constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; +contract UpgradeableProxyUtilsTest is Test { + address constant internal CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; + ProxyAdmin internal admin; + + function setUp() public { + admin = new ProxyAdmin(); + } function testTransparent() public { address proxy = UpgradeableProxyUtils.deployTransparentProxy( "Greeter.sol", - address(this), + address(admin), abi.encodeCall(Greeter.initialize, ("hello")) ); Greeter instance = Greeter(proxy); @@ -24,34 +29,25 @@ contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { address adminAddress = UpgradeableProxyUtils.getAdminAddress(proxy); assertFalse(adminAddress == address(0)); - - vm.startPrank(address(420)); assertEq(instance.greeting(), "hello"); - vm.stopPrank(); - vm.startPrank(ProxyAdmin(address(this)).owner()); UpgradeableProxyUtils.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ())); - vm.stopPrank(); address implAddressV2 = UpgradeableProxyUtils.getImplementationAddress(proxy); assertEq(UpgradeableProxyUtils.getAdminAddress(proxy), adminAddress); - - vm.startPrank(address(420)); assertEq(instance.greeting(), "resetted"); - vm.stopPrank(); assertFalse(implAddressV2 == implAddressV1); } function testBeacon() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(this), abi.encode()); + address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); address implAddressV1 = IBeacon(beacon).implementation(); address proxy = UpgradeableProxyUtils.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); Greeter instance = Greeter(proxy); assertEq(UpgradeableProxyUtils.getBeaconAddress(proxy), beacon); - assertEq(instance.greeting(), "hello"); UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol"); @@ -64,7 +60,7 @@ contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { } function testUpgradeBeaconWithoutCaller() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(this), abi.encode()); + address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol", abi.encode()); } @@ -76,6 +72,7 @@ contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { abi.encodeCall(WithConstructor.initialize, (456)), constructorData ); + assertEq(WithConstructor(proxy).a(), 123); assertEq(WithConstructor(proxy).b(), 456); } @@ -84,6 +81,7 @@ contract UpgradeableProxyUtilsTest is ProxyAdmin, Test { /// Can access getCode by File:Contract bytes memory constructorData = abi.encode(123); address proxy = UpgradeableProxyUtils.deployTransparentProxy("NoInitializer.sol", msg.sender, "", constructorData); + assertEq(WithConstructor(proxy).a(), 123); } From c354f4b987d9cfa493fa380fb18ebef14cfb2fc7 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 19 Feb 2024 17:36:25 -0500 Subject: [PATCH 6/9] chore: remove unused variable --- test/utils/UpgradeableProxyUtils.t.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/utils/UpgradeableProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol index ed79a291..f0197078 100644 --- a/test/utils/UpgradeableProxyUtils.t.sol +++ b/test/utils/UpgradeableProxyUtils.t.sol @@ -11,7 +11,6 @@ import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; contract UpgradeableProxyUtilsTest is Test { - address constant internal CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; ProxyAdmin internal admin; function setUp() public { From 25ae11b239536e757f883815df77fa202ee2b61d Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 20 Feb 2024 07:52:47 -0500 Subject: [PATCH 7/9] chore: nits --- test/utils/UpgradeableProxyUtils.sol | 94 +++++++++++----------------- 1 file changed, 36 insertions(+), 58 deletions(-) diff --git a/test/utils/UpgradeableProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol index 20ebafda..253f005f 100644 --- a/test/utils/UpgradeableProxyUtils.sol +++ b/test/utils/UpgradeableProxyUtils.sol @@ -118,7 +118,6 @@ library UpgradeableProxyUtils { } /** * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. - * * @param proxy Address of a transparent proxy * @return Admin address */ @@ -129,7 +128,6 @@ library UpgradeableProxyUtils { /** * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. - * * @param proxy Address of a transparent or UUPS proxy * @return Implementation address */ @@ -140,7 +138,6 @@ library UpgradeableProxyUtils { /** * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. - * * @param proxy Address of a beacon proxy * @return Beacon address */ @@ -149,45 +146,15 @@ library UpgradeableProxyUtils { return address(uint160(uint256(beaconSlot))); } - function _deploy( - string memory contractName, - bytes memory constructorData - ) private returns (address) { - bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); - address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); - if (deployedAddress == address(0)) { - revert( - string.concat( - "Failed to deploy contract ", - contractName, - ' using constructor data "', - string(constructorData), - '"' - ) - ); - } - return deployedAddress; - } - - function _deployFromBytecode(bytes memory bytecode) private returns (address) { - address addr; - assembly { - addr := create(0, add(bytecode, 32), mload(bytecode)) - } - return addr; - } - - /** - * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. - * - * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - * + /** + * @dev Upgrades a proxy to a new implementation contract. * @param proxy Address of the proxy to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade + * @param constructorData abi encoded constructor arguments for deploying the implementation contract */ function upgradeProxy(address proxy, string memory contractName, bytes memory data, bytes memory constructorData) internal { - address newImpl = prepareUpgrade(contractName, constructorData); + address newImpl = _deploy(contractName, constructorData); bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); if (adminSlot == bytes32(0)) { @@ -201,10 +168,7 @@ library UpgradeableProxyUtils { } /** - * @dev Upgrades a proxy to a new implementation contract. Only supported for UUPS or transparent proxies. - * - * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - * + * @dev Upgrades a proxy to a new implementation contract. * @param proxy Address of the proxy to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade @@ -215,14 +179,12 @@ library UpgradeableProxyUtils { /** * @dev Upgrades a beacon to a new implementation contract. - * - * Requires that either the `referenceContract` option is set, or the new implementation contract has a `@custom:oz-upgrades-from ` annotation. - * * @param beacon Address of the beacon to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory + * @param constructorData abi encoded constructor arguments for deploying the implementation contract */ function upgradeBeacon(address beacon, string memory contractName, bytes memory constructorData) internal { - address newImpl = prepareUpgrade(contractName, constructorData); + address newImpl = _deploy(contractName, constructorData); UpgradeableBeacon(beacon).upgradeTo(newImpl); } @@ -234,21 +196,37 @@ library UpgradeableProxyUtils { function upgradeBeacon(address beacon, string memory contractName ) internal { upgradeBeacon(beacon, contractName, ""); } - /** - * @dev Validates a new implementation contract in comparison with a reference contract, deploys the new implementation contract, - * and returns its address. - * - * Requires that either the `referenceContract` option is set, or the contract has a `@custom:oz-upgrades-from ` annotation. - * - * Use this method to prepare an upgrade to be run from an admin address you do not control directly or cannot use from your deployment environment. - * - * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @return Address of the new implementation contract - */ - function prepareUpgrade(string memory contractName, bytes memory constructorData) internal returns (address) { - return _deploy(contractName, constructorData); + + function _deploy( + string memory contractName, + bytes memory constructorData + ) private returns (address) { + bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); + address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); + if (deployedAddress == address(0)) { + revert( + string.concat( + "Failed to deploy contract ", + contractName, + ' using constructor data "', + string(constructorData), + '"' + ) + ); + } + return deployedAddress; + } + + function _deployFromBytecode(bytes memory bytecode) private returns (address) { + address addr; + assembly { + addr := create(0, add(bytecode, 32), mload(bytecode)) + } + return addr; } + + /** * @dev Precompile proxy contracts so that they can be deployed by name via the `_deploy` function. * From 18970a79919f3c4baa5da47a20a8b147c43d02c1 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 20 Feb 2024 07:53:52 -0500 Subject: [PATCH 8/9] chore: prettier --- test/utils/UpgradeableProxyUtils.sol | 60 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/test/utils/UpgradeableProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol index 253f005f..696fb658 100644 --- a/test/utils/UpgradeableProxyUtils.sol +++ b/test/utils/UpgradeableProxyUtils.sol @@ -10,7 +10,7 @@ import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transpa /// Modified from the Openzeppelin foundry upgrades library /// Modifications: -/// - Made compatible with OZ ^4.x releases +/// - Made compatible with OZ ^4.x releases /// - Removed OZ Defender functionality library UpgradeableProxyUtils { address private constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; @@ -146,19 +146,23 @@ library UpgradeableProxyUtils { return address(uint160(uint256(beaconSlot))); } - /** - * @dev Upgrades a proxy to a new implementation contract. + /** + * @dev Upgrades a proxy to a new implementation contract. * @param proxy Address of the proxy to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - * @param constructorData abi encoded constructor arguments for deploying the implementation contract + * @param constructorData abi encoded constructor arguments for deploying the implementation contract */ - function upgradeProxy(address proxy, string memory contractName, bytes memory data, bytes memory constructorData) internal { + function upgradeProxy( + address proxy, + string memory contractName, + bytes memory data, + bytes memory constructorData + ) internal { address newImpl = _deploy(contractName, constructorData); bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); if (adminSlot == bytes32(0)) { - // No admin contract: upgrade directly using interface TransparentUpgradeableProxy(payable(proxy)).upgradeToAndCall(newImpl, data); } else { @@ -168,7 +172,7 @@ library UpgradeableProxyUtils { } /** - * @dev Upgrades a proxy to a new implementation contract. + * @dev Upgrades a proxy to a new implementation contract. * @param proxy Address of the proxy to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade @@ -181,41 +185,37 @@ library UpgradeableProxyUtils { * @dev Upgrades a beacon to a new implementation contract. * @param beacon Address of the beacon to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param constructorData abi encoded constructor arguments for deploying the implementation contract + * @param constructorData abi encoded constructor arguments for deploying the implementation contract */ function upgradeBeacon(address beacon, string memory contractName, bytes memory constructorData) internal { address newImpl = _deploy(contractName, constructorData); UpgradeableBeacon(beacon).upgradeTo(newImpl); } - - /* + /* * @param beacon Address of the beacon to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory */ - function upgradeBeacon(address beacon, string memory contractName ) internal { + function upgradeBeacon(address beacon, string memory contractName) internal { upgradeBeacon(beacon, contractName, ""); } - function _deploy( - string memory contractName, - bytes memory constructorData - ) private returns (address) { - bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); - address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); - if (deployedAddress == address(0)) { - revert( - string.concat( - "Failed to deploy contract ", - contractName, - ' using constructor data "', - string(constructorData), - '"' - ) - ); - } - return deployedAddress; + function _deploy(string memory contractName, bytes memory constructorData) private returns (address) { + bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); + address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); + if (deployedAddress == address(0)) { + revert( + string.concat( + "Failed to deploy contract ", + contractName, + ' using constructor data "', + string(constructorData), + '"' + ) + ); } + return deployedAddress; + } function _deployFromBytecode(bytes memory bytecode) private returns (address) { address addr; @@ -225,8 +225,6 @@ library UpgradeableProxyUtils { return addr; } - - /** * @dev Precompile proxy contracts so that they can be deployed by name via the `_deploy` function. * From c0ecbecdc384aeb7b584db379776c3bf0816e139 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 20 Feb 2024 14:36:20 -0500 Subject: [PATCH 9/9] chore: rename parmas --- test/utils/UpgradeableProxyUtils.sol | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/utils/UpgradeableProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol index 696fb658..6ce5471a 100644 --- a/test/utils/UpgradeableProxyUtils.sol +++ b/test/utils/UpgradeableProxyUtils.sol @@ -68,9 +68,9 @@ library UpgradeableProxyUtils { string memory contractName, address initialOwner, bytes memory initializerData, - bytes memory constructorData + bytes memory implConstructorArgs ) internal returns (address) { - address impl = deployImplementation(contractName, constructorData); + address impl = deployImplementation(contractName, implConstructorArgs); return address( _deploy( @@ -90,9 +90,9 @@ library UpgradeableProxyUtils { function deployBeacon( string memory contractName, address initialOwner, - bytes memory constructorData + bytes memory implConstructorArgs ) internal returns (address) { - address impl = deployImplementation(contractName, constructorData); + address impl = deployImplementation(contractName, implConstructorArgs); return _deploy("UpgradeableBeacon.sol:UpgradeableBeacon", abi.encode(impl, initialOwner)); } @@ -113,8 +113,8 @@ library UpgradeableProxyUtils { * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory * @return Address of the implementation contract */ - function deployImplementation(string memory contractName, bytes memory constructorData) internal returns (address) { - return _deploy(contractName, constructorData); + function deployImplementation(string memory contractName, bytes memory implConstructorArgs) internal returns (address) { + return _deploy(contractName, implConstructorArgs); } /** * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. @@ -151,15 +151,15 @@ library UpgradeableProxyUtils { * @param proxy Address of the proxy to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - * @param constructorData abi encoded constructor arguments for deploying the implementation contract + * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract */ function upgradeProxy( address proxy, string memory contractName, bytes memory data, - bytes memory constructorData + bytes memory implConstructorArgs ) internal { - address newImpl = _deploy(contractName, constructorData); + address newImpl = _deploy(contractName, implConstructorArgs); bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); if (adminSlot == bytes32(0)) { @@ -185,10 +185,10 @@ library UpgradeableProxyUtils { * @dev Upgrades a beacon to a new implementation contract. * @param beacon Address of the beacon to upgrade * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param constructorData abi encoded constructor arguments for deploying the implementation contract + * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract */ - function upgradeBeacon(address beacon, string memory contractName, bytes memory constructorData) internal { - address newImpl = _deploy(contractName, constructorData); + function upgradeBeacon(address beacon, string memory contractName, bytes memory implConstructorArgs) internal { + address newImpl = _deploy(contractName, implConstructorArgs); UpgradeableBeacon(beacon).upgradeTo(newImpl); } @@ -200,16 +200,16 @@ library UpgradeableProxyUtils { upgradeBeacon(beacon, contractName, ""); } - function _deploy(string memory contractName, bytes memory constructorData) private returns (address) { + function _deploy(string memory contractName, bytes memory implConstructorArgs) private returns (address) { bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); - address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, constructorData)); + address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, implConstructorArgs)); if (deployedAddress == address(0)) { revert( string.concat( "Failed to deploy contract ", contractName, ' using constructor data "', - string(constructorData), + string(implConstructorArgs), '"' ) );