From c020802a7627d94765f382ef5aa792a5c0d947ca Mon Sep 17 00:00:00 2001 From: Cheyenne Atapour Date: Tue, 15 Oct 2024 01:02:20 -0700 Subject: [PATCH] feat: Upgrade Remote Pool to include RateLimitAdmin (#16) * feat: enable remote rateLimitAdmin * chore: prettier * fix: remove unnecessary modifer * chore: reorder state variables * feat: upgrade remote pool * test: add tests after upgrading * feat: reserve storage space for future upgrades * diff: Update diff for UpgradeableBurnMintTokenPool * ci: Fix ci * ci: Fix ci * test: show init reverting after upgrade * fix: remove gap * fix: return to original test suite * test: simple tests showing upgrade * fix: add and correct comments * fix: comments to match parent contract * chore: prettier --------- Co-authored-by: miguelmtzinf --- .../GHO/UpgradeableBurnMintTokenPoolOld.sol | 56 +++++++++++++ .../ccip/test/pools/GHO/GhoBaseTest.t.sol | 31 +++++++ .../pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol | 81 +++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPoolOld.sol create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPoolOld.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPoolOld.sol new file mode 100644 index 0000000000..16ab280431 --- /dev/null +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPoolOld.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; + +import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; +import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; +import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; + +import {IRouter} from "../../interfaces/IRouter.sol"; + +/// @title UpgradeableBurnMintTokenPoolOld +/// @author Aave Labs +/// @notice Upgradeable version of Chainlink's CCIP BurnMintTokenPool +/// @dev Contract adaptations: +/// - Implementation of Initializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage +contract UpgradeableBurnMintTokenPoolOld is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { + string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; + + /// @dev Constructor + /// @param token The bridgeable token that is managed by this pool. + /// @param armProxy The address of the arm proxy + /// @param allowlistEnabled True if pool is set to access-controlled mode, false otherwise + constructor( + address token, + address armProxy, + bool allowlistEnabled + ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} + + /// @dev Initializer + /// @dev The address passed as `owner` must accept ownership after initialization. + /// @dev The `allowlist` is only effective if pool is set to access-controlled mode + /// @param owner The address of the owner + /// @param allowlist A set of addresses allowed to trigger lockOrBurn as original senders + /// @param router The address of the router + function initialize(address owner, address[] memory allowlist, address router) public virtual initializer { + if (owner == address(0)) revert ZeroAddressNotAllowed(); + if (router == address(0)) revert ZeroAddressNotAllowed(); + _transferOwnership(owner); + + s_router = IRouter(router); + + // Pool can be set as permissioned or permissionless at deployment time only to save hot-path gas. + if (i_allowlistEnabled) { + _applyAllowListUpdates(new address[](0), allowlist); + } + } + + /// @inheritdoc UpgradeableBurnMintTokenPoolAbstract + function _burn(uint256 amount) internal virtual override { + IBurnMintERC20(address(i_token)).burn(amount); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoBaseTest.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoBaseTest.t.sol index 66d6fc63b5..52c22fc4bd 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoBaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoBaseTest.t.sol @@ -8,6 +8,7 @@ import {IBurnMintERC20} from "../../../../shared/token/ERC20/IBurnMintERC20.sol" import {IPool} from "../../../interfaces/pools/IPool.sol"; import {UpgradeableLockReleaseTokenPool} from "../../../pools/GHO/UpgradeableLockReleaseTokenPool.sol"; import {UpgradeableBurnMintTokenPool} from "../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {UpgradeableBurnMintTokenPoolOld} from "../../../pools/GHO/UpgradeableBurnMintTokenPoolOld.sol"; import {UpgradeableTokenPool} from "../../../pools/GHO/UpgradeableTokenPool.sol"; import {RateLimiter} from "../../../libraries/RateLimiter.sol"; import {BaseTest} from "../../BaseTest.t.sol"; @@ -65,6 +66,36 @@ abstract contract GhoBaseTest is BaseTest { return address(tokenPoolProxy); } + function _deployUpgradeableBurnMintTokenPoolOld( + address ghoToken, + address arm, + address router, + address owner, + address proxyAdmin + ) internal returns (address) { + // Deploy BurnMintTokenPool for GHO token on source chain + UpgradeableBurnMintTokenPoolOld tokenPoolImpl = new UpgradeableBurnMintTokenPoolOld(ghoToken, arm, false); + // proxy deploy and init + address[] memory emptyArray = new address[](0); + bytes memory tokenPoolInitParams = abi.encodeWithSignature( + "initialize(address,address[],address)", + owner, + emptyArray, + router + ); + TransparentUpgradeableProxy tokenPoolProxy = new TransparentUpgradeableProxy( + address(tokenPoolImpl), + proxyAdmin, + tokenPoolInitParams + ); + // Manage ownership + changePrank(owner); + UpgradeableBurnMintTokenPoolOld(address(tokenPoolProxy)).acceptOwnership(); + vm.stopPrank(); + + return address(tokenPoolProxy); + } + function _deployUpgradeableLockReleaseTokenPool( address ghoToken, address arm, diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol new file mode 100644 index 0000000000..763f6303bd --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; + +import {UpgradeableBurnMintTokenPool} from "../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {GhoTokenPoolRemoteSetup} from "./GhoTokenPoolRemoteSetup.t.sol"; + +contract GhoTokenPoolRemoteUpgrade is GhoTokenPoolRemoteSetup { + // Unable to call setRateLimitAdmin() before upgrade + function testSetRateLimitAdminRevertsBeforeUpgrade() public { + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( + address(s_burnMintERC677), + address(s_mockARM), + address(s_sourceRouter), + AAVE_DAO, + PROXY_ADMIN + ) + ); + vm.prank(AAVE_DAO); + vm.expectRevert(); + s_pool.setRateLimitAdmin(AAVE_DAO); + } + + // Able to call setRateLimitAdmin() after upgrade + function testUpgradeAndSetRateLimitAdmin() public { + // Assume existing remote pool as is deployed + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( + address(s_burnMintERC677), + address(s_mockARM), + address(s_sourceRouter), + AAVE_DAO, + PROXY_ADMIN + ) + ); + + // Deploy new implementation + UpgradeableBurnMintTokenPool tokenPoolImpl = new UpgradeableBurnMintTokenPool( + address(s_burnMintERC677), + address(s_mockARM), + false + ); + // Do the upgrade + vm.prank(PROXY_ADMIN); + TransparentUpgradeableProxy(payable(address(s_pool))).upgradeTo(address(tokenPoolImpl)); + + // Set rate limit admin now works + vm.prank(AAVE_DAO); + s_pool.setRateLimitAdmin(OWNER); + assertEq(OWNER, s_pool.getRateLimitAdmin()); + } + + // Unable to call initialize() on proxy after upgrade + function testInitializeRevertsAfterUpgrade() public { + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( + address(s_burnMintERC677), + address(s_mockARM), + address(s_sourceRouter), + AAVE_DAO, + PROXY_ADMIN + ) + ); + + // Deploy new implementation + UpgradeableBurnMintTokenPool tokenPoolImpl = new UpgradeableBurnMintTokenPool( + address(s_burnMintERC677), + address(s_mockARM), + false + ); + // Do the upgrade + vm.prank(PROXY_ADMIN); + TransparentUpgradeableProxy(payable(address(s_pool))).upgradeTo(address(tokenPoolImpl)); + + vm.startPrank(OWNER); + vm.expectRevert("Initializable: contract is already initialized"); + s_pool.initialize(OWNER, new address[](0), address(s_sourceRouter)); + } +}