From acd5d8279603b8928cd10c0f36e1acf8376a1c6a Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Thu, 11 Jul 2024 13:45:19 -0700 Subject: [PATCH 01/53] feat: enable remote rateLimitAdmin --- .../src/v0.8/ccip/pools/BurnMintTokenPool.sol | 2 + .../GHO/UpgradeableBurnMintTokenPool.sol | 35 +++++ .../test/pools/GHO/GhoTokenPoolRemote.t.sol | 138 ++++++++++++++++++ 3 files changed, 175 insertions(+) diff --git a/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol index 9af0f22f4c..9c101f1626 100644 --- a/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol @@ -15,6 +15,8 @@ import {BurnMintTokenPoolAbstract} from "./BurnMintTokenPoolAbstract.sol"; contract BurnMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion { string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; + error Unauthorized(address caller); + constructor( IBurnMintERC20 token, address[] memory allowlist, diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 58be87812f..91b4a28bb0 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -8,6 +8,7 @@ import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; import {IRouter} from "../../interfaces/IRouter.sol"; @@ -20,6 +21,8 @@ import {IRouter} from "../../interfaces/IRouter.sol"; contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; + error Unauthorized(address caller); + /// @dev Constructor /// @param token The bridgeable token that is managed by this pool. /// @param armProxy The address of the arm proxy @@ -30,6 +33,10 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken bool allowlistEnabled ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} + /// @notice The address of the rate limiter admin. + /// @dev Can be address(0) if none is configured. + address internal s_rateLimitAdmin; + /// @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 @@ -49,6 +56,34 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken } } + /// @notice Sets the rate limiter admin address. + /// @dev Only callable by the owner. + /// @param rateLimitAdmin The new rate limiter admin address. + function setRateLimitAdmin(address rateLimitAdmin) external onlyOwner { + s_rateLimitAdmin = rateLimitAdmin; + } + + /// @notice Gets the rate limiter admin address. + function getRateLimitAdmin() external view returns (address) { + return s_rateLimitAdmin; + } + + /// @notice Sets the rate limiter admin address. + /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal + /// onlyAdmin check in the base implementation to also allow the rate limiter admin. + /// @param remoteChainSelector The remote chain selector for which the rate limits apply. + /// @param outboundConfig The new outbound rate limiter config. + /// @param inboundConfig The new inbound rate limiter config. + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + RateLimiter.Config memory outboundConfig, + RateLimiter.Config memory inboundConfig + ) external override { + if (msg.sender != s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + + _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); + } + /// @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/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index dd784e68c1..58a99f253d 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -242,3 +242,141 @@ contract GhoTokenPoolEthereum_upgradeability is GhoTokenPoolRemoteSetup { assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN, "Unauthorized admin change"); } } + +contract GhoTokenPoolRemote_setChainRateLimiterConfig is GhoTokenPoolRemoteSetup { + event ConfigChanged(RateLimiter.Config); + event ChainConfigured( + uint64 chainSelector, + RateLimiter.Config outboundRateLimiterConfig, + RateLimiter.Config inboundRateLimiterConfig + ); + + uint64 internal s_remoteChainSelector; + + function setUp() public virtual override { + GhoTokenPoolRemoteSetup.setUp(); + UpgradeableTokenPool.ChainUpdate[] memory chainUpdates = new UpgradeableTokenPool.ChainUpdate[](1); + s_remoteChainSelector = 123124; + chainUpdates[0] = UpgradeableTokenPool.ChainUpdate({ + remoteChainSelector: s_remoteChainSelector, + allowed: true, + outboundRateLimiterConfig: getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: getInboundRateLimiterConfig() + }); + changePrank(AAVE_DAO); + s_pool.applyChainUpdates(chainUpdates); + changePrank(OWNER); + } + + function testFuzz_SetChainRateLimiterConfigSuccess(uint128 capacity, uint128 rate, uint32 newTime) public { + // Cap the lower bound to 4 so 4/2 is still >= 2 + vm.assume(capacity >= 4); + // Cap the lower bound to 2 so 2/2 is still >= 1 + rate = uint128(bound(rate, 2, capacity - 2)); + // Bucket updates only work on increasing time + newTime = uint32(bound(newTime, block.timestamp + 1, type(uint32).max)); + vm.warp(newTime); + + uint256 oldOutboundTokens = s_pool.getCurrentOutboundRateLimiterState(s_remoteChainSelector).tokens; + uint256 oldInboundTokens = s_pool.getCurrentInboundRateLimiterState(s_remoteChainSelector).tokens; + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({isEnabled: true, capacity: capacity, rate: rate}); + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: true, + capacity: capacity / 2, + rate: rate / 2 + }); + + vm.expectEmit(); + emit ConfigChanged(newOutboundConfig); + vm.expectEmit(); + emit ConfigChanged(newInboundConfig); + vm.expectEmit(); + emit ChainConfigured(s_remoteChainSelector, newOutboundConfig, newInboundConfig); + + changePrank(AAVE_DAO); + s_pool.setChainRateLimiterConfig(s_remoteChainSelector, newOutboundConfig, newInboundConfig); + + uint256 expectedTokens = RateLimiter._min(newOutboundConfig.capacity, oldOutboundTokens); + + RateLimiter.TokenBucket memory bucket = s_pool.getCurrentOutboundRateLimiterState(s_remoteChainSelector); + assertEq(bucket.capacity, newOutboundConfig.capacity); + assertEq(bucket.rate, newOutboundConfig.rate); + assertEq(bucket.tokens, expectedTokens); + assertEq(bucket.lastUpdated, newTime); + + expectedTokens = RateLimiter._min(newInboundConfig.capacity, oldInboundTokens); + + bucket = s_pool.getCurrentInboundRateLimiterState(s_remoteChainSelector); + assertEq(bucket.capacity, newInboundConfig.capacity); + assertEq(bucket.rate, newInboundConfig.rate); + assertEq(bucket.tokens, expectedTokens); + assertEq(bucket.lastUpdated, newTime); + } + + function testOnlyOwnerOrRateLimitAdminSuccess() public { + address rateLimiterAdmin = address(28973509103597907); + + changePrank(AAVE_DAO); + s_pool.setRateLimitAdmin(rateLimiterAdmin); + + changePrank(rateLimiterAdmin); + + s_pool.setChainRateLimiterConfig( + s_remoteChainSelector, + getOutboundRateLimiterConfig(), + getInboundRateLimiterConfig() + ); + + changePrank(AAVE_DAO); + + s_pool.setChainRateLimiterConfig( + s_remoteChainSelector, + getOutboundRateLimiterConfig(), + getInboundRateLimiterConfig() + ); + } + + // Reverts + + function testOnlyOwnerReverts() public { + changePrank(STRANGER); + + vm.expectRevert(abi.encodeWithSelector(BurnMintTokenPool.Unauthorized.selector, STRANGER)); + s_pool.setChainRateLimiterConfig( + s_remoteChainSelector, + getOutboundRateLimiterConfig(), + getInboundRateLimiterConfig() + ); + } + + function testNonExistentChainReverts() public { + uint64 wrongChainSelector = 9084102894; + + vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.NonExistentChain.selector, wrongChainSelector)); + changePrank(AAVE_DAO); + s_pool.setChainRateLimiterConfig( + wrongChainSelector, + getOutboundRateLimiterConfig(), + getInboundRateLimiterConfig() + ); + } +} + +contract GhoTokenPoolRemote_setRateLimitAdmin is GhoTokenPoolRemoteSetup { + function testSetRateLimitAdminSuccess() public { + assertEq(address(0), s_pool.getRateLimitAdmin()); + changePrank(AAVE_DAO); + s_pool.setRateLimitAdmin(OWNER); + assertEq(OWNER, s_pool.getRateLimitAdmin()); + } + + // Reverts + + function testSetRateLimitAdminReverts() public { + vm.startPrank(STRANGER); + + vm.expectRevert("Only callable by owner"); + s_pool.setRateLimitAdmin(STRANGER); + } +} From a03bad2cbf1f35576fdf341deeae3c1d073828e6 Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Thu, 11 Jul 2024 13:48:41 -0700 Subject: [PATCH 02/53] chore: prettier --- contracts/src/v0.8/ccip/test/BaseTest.t.sol | 3 --- .../src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/BaseTest.t.sol b/contracts/src/v0.8/ccip/test/BaseTest.t.sol index 0dd265092b..ad7cf3a2a1 100644 --- a/contracts/src/v0.8/ccip/test/BaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/BaseTest.t.sol @@ -8,7 +8,6 @@ import {StdUtils} from "forge-std/StdUtils.sol"; import {MockARM} from "./mocks/MockARM.sol"; import {StructFactory} from "./StructFactory.sol"; - contract BaseTest is Test, StructFactory { bool private s_baseTestInitialized; @@ -30,6 +29,4 @@ contract BaseTest is Test, StructFactory { s_mockARM = new MockARM(); } - - } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index 58a99f253d..78d94e82c7 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -355,11 +355,7 @@ contract GhoTokenPoolRemote_setChainRateLimiterConfig is GhoTokenPoolRemoteSetup vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.NonExistentChain.selector, wrongChainSelector)); changePrank(AAVE_DAO); - s_pool.setChainRateLimiterConfig( - wrongChainSelector, - getOutboundRateLimiterConfig(), - getInboundRateLimiterConfig() - ); + s_pool.setChainRateLimiterConfig(wrongChainSelector, getOutboundRateLimiterConfig(), getInboundRateLimiterConfig()); } } From 1268d85a691ea683147ad527b588a6c730ec8891 Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Fri, 12 Jul 2024 02:09:34 -0700 Subject: [PATCH 03/53] fix: remove unnecessary modifer --- contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol | 2 -- contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol index 9c101f1626..9af0f22f4c 100644 --- a/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol @@ -15,8 +15,6 @@ import {BurnMintTokenPoolAbstract} from "./BurnMintTokenPoolAbstract.sol"; contract BurnMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion { string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; - error Unauthorized(address caller); - constructor( IBurnMintERC20 token, address[] memory allowlist, diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index 78d94e82c7..773528c715 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -342,7 +342,7 @@ contract GhoTokenPoolRemote_setChainRateLimiterConfig is GhoTokenPoolRemoteSetup function testOnlyOwnerReverts() public { changePrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(BurnMintTokenPool.Unauthorized.selector, STRANGER)); + vm.expectRevert(abi.encodeWithSelector(UpgradeableBurnMintTokenPool.Unauthorized.selector, STRANGER)); s_pool.setChainRateLimiterConfig( s_remoteChainSelector, getOutboundRateLimiterConfig(), From 790bcef3690eb26bc962ddd26606e987704af13d Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Wed, 17 Jul 2024 10:27:50 -0700 Subject: [PATCH 04/53] chore: reorder state variables --- .../ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 91b4a28bb0..a46ff915e5 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -19,9 +19,13 @@ import {IRouter} from "../../interfaces/IRouter.sol"; /// - Implementation of Initializable to allow upgrades /// - Move of allowlist and router definition to initialization stage contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { + error Unauthorized(address caller); + string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; - error Unauthorized(address caller); + /// @notice The address of the rate limiter admin. + /// @dev Can be address(0) if none is configured. + address internal s_rateLimitAdmin; /// @dev Constructor /// @param token The bridgeable token that is managed by this pool. @@ -33,10 +37,6 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken bool allowlistEnabled ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} - /// @notice The address of the rate limiter admin. - /// @dev Can be address(0) if none is configured. - address internal s_rateLimitAdmin; - /// @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 From 19829fd87d7faa957de71da67ab45326124a0f34 Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Wed, 17 Jul 2024 11:39:31 -0700 Subject: [PATCH 05/53] feat: upgrade remote pool --- .../GHO/UpgradeableBurnMintTokenPoolOld.sol | 56 +++++++++++++ .../ccip/test/pools/GHO/GhoBaseTest.t.sol | 45 +++++++++++ .../pools/GHO/GhoTokenPoolRemoteOld.t.sol | 49 ++++++++++++ .../GHO/GhoTokenPoolRemoteSetupOld.t.sol | 78 +++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPoolOld.sol create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.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..921680f9c8 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,50 @@ 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 + vm.stopPrank(); + vm.prank(owner); + UpgradeableBurnMintTokenPoolOld(address(tokenPoolProxy)).acceptOwnership(); + vm.startPrank(OWNER); + + return address(tokenPoolProxy); + } + + function _upgradeUpgradeableBurnMintTokenPool( + address payable tokenPoolProxy, + address ghoToken, + address arm, + address proxyAdmin + ) internal { + // Deploy BurnMintTokenPool for GHO token on source chain + UpgradeableBurnMintTokenPool tokenPoolImpl = new UpgradeableBurnMintTokenPool(ghoToken, arm, false); + // proxy upgrade + vm.startPrank(proxyAdmin); + TransparentUpgradeableProxy(tokenPoolProxy).upgradeTo(address(tokenPoolImpl)); + } + function _deployUpgradeableLockReleaseTokenPool( address ghoToken, address arm, diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol new file mode 100644 index 0000000000..6bea004e3f --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {GhoToken} from "@aave/gho-core/gho/GhoToken.sol"; +import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; + +import {stdError} from "forge-std/Test.sol"; +import {MockUpgradeable} from "../../mocks/MockUpgradeable.sol"; +import {UpgradeableTokenPool} from "../../../pools/GHO/UpgradeableTokenPool.sol"; +import {EVM2EVMOnRamp} from "../../../onRamp/EVM2EVMOnRamp.sol"; +import {EVM2EVMOffRamp} from "../../../offRamp/EVM2EVMOffRamp.sol"; +import {BurnMintTokenPool} from "../../../pools/BurnMintTokenPool.sol"; +import {UpgradeableBurnMintTokenPool} from "../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {GhoTokenPoolRemoteSetupOld} from "./GhoTokenPoolRemoteSetupOld.t.sol"; + +contract GhoTokenPoolRemoteOld_setRateLimitAdmin is GhoTokenPoolRemoteSetupOld { + /*function testSetRateLimitAdminSuccess() public { + assertEq(address(0), s_pool.getRateLimitAdmin()); + changePrank(AAVE_DAO); + s_pool.setRateLimitAdmin(OWNER); + assertEq(OWNER, s_pool.getRateLimitAdmin()); + }*/ + + // Reverts + + // Should fail because old implementation does not have rate limiter + function testSetRateLimitRevert() public { + changePrank(AAVE_DAO); + vm.expectRevert(); + s_pool.setRateLimitAdmin(OWNER); + } + + function testSetRateLimitAfterUpgrade() public { + _upgradeUpgradeableBurnMintTokenPool(payable(address(s_pool)), address(s_burnMintERC677), ARM_PROXY, PROXY_ADMIN); + changePrank(AAVE_DAO); + s_pool.setRateLimitAdmin(OWNER); + assertEq(OWNER, s_pool.getRateLimitAdmin()); + } + + /* + function testSetRateLimitAdminReverts() public { + vm.startPrank(STRANGER); + + vm.expectRevert("Only callable by owner"); + s_pool.setRateLimitAdmin(STRANGER); + } + */ +} diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol new file mode 100644 index 0000000000..784481b362 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {GhoToken} from "@aave/gho-core/gho/GhoToken.sol"; +import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; + +import {stdError} from "forge-std/Test.sol"; +import {UpgradeableTokenPool} from "../../../pools/GHO/UpgradeableTokenPool.sol"; +import {Router} from "../../../Router.sol"; +import {BurnMintERC677} from "../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {UpgradeableBurnMintTokenPoolOld} from "../../../pools/GHO/UpgradeableBurnMintTokenPoolOld.sol"; +import {UpgradeableBurnMintTokenPool} from "../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {RouterSetup} from "../../router/RouterSetup.t.sol"; +import {BaseTest} from "../../BaseTest.t.sol"; +import {GhoBaseTest} from "./GhoBaseTest.t.sol"; + +contract GhoTokenPoolRemoteSetupOld is RouterSetup, GhoBaseTest { + event Transfer(address indexed from, address indexed to, uint256 value); + event TokensConsumed(uint256 tokens); + event Burned(address indexed sender, uint256 amount); + + BurnMintERC677 internal s_burnMintERC677; + address internal s_burnMintOffRamp = makeAddr("burn_mint_offRamp"); + address internal s_burnMintOnRamp = makeAddr("burn_mint_onRamp"); + + UpgradeableBurnMintTokenPool internal s_pool; + + function setUp() public virtual override(RouterSetup, BaseTest) { + RouterSetup.setUp(); + + // GHO deployment + GhoToken ghoToken = new GhoToken(AAVE_DAO); + s_burnMintERC677 = BurnMintERC677(address(ghoToken)); + + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( + address(s_burnMintERC677), + address(s_mockARM), + address(s_sourceRouter), + AAVE_DAO, + PROXY_ADMIN + ) + ); + + // Give mint and burn privileges to source UpgradeableTokenPool (GHO-specific related) + vm.stopPrank(); + vm.startPrank(AAVE_DAO); + GhoToken(address(s_burnMintERC677)).grantRole( + GhoToken(address(s_burnMintERC677)).FACILITATOR_MANAGER_ROLE(), + AAVE_DAO + ); + GhoToken(address(s_burnMintERC677)).addFacilitator(address(s_pool), "UpgradeableTokenPool", type(uint128).max); + vm.stopPrank(); + + _applyChainUpdates(address(s_pool)); + } + + function _applyChainUpdates(address pool) internal { + UpgradeableTokenPool.ChainUpdate[] memory chains = new UpgradeableTokenPool.ChainUpdate[](1); + chains[0] = UpgradeableTokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + allowed: true, + outboundRateLimiterConfig: getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: getInboundRateLimiterConfig() + }); + + vm.startPrank(AAVE_DAO); + UpgradeableBurnMintTokenPool(pool).applyChainUpdates(chains); + vm.stopPrank(); + vm.startPrank(OWNER); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_burnMintOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: DEST_CHAIN_SELECTOR, offRamp: s_burnMintOffRamp}); + s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } +} From b075172f024c997bba7aee75132a452db6013618 Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Wed, 17 Jul 2024 12:39:47 -0700 Subject: [PATCH 06/53] test: add tests after upgrading --- .../ccip/test/pools/GHO/GhoBaseTest.t.sol | 7 +- .../pools/GHO/GhoTokenPoolRemoteOld.t.sol | 368 +++++++++++++++++- .../GHO/GhoTokenPoolRemoteSetupOld.t.sol | 32 ++ 3 files changed, 388 insertions(+), 19 deletions(-) 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 921680f9c8..b1721855b4 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 @@ -89,10 +89,9 @@ abstract contract GhoBaseTest is BaseTest { tokenPoolInitParams ); // Manage ownership - vm.stopPrank(); - vm.prank(owner); + changePrank(owner); UpgradeableBurnMintTokenPoolOld(address(tokenPoolProxy)).acceptOwnership(); - vm.startPrank(OWNER); + vm.stopPrank(); return address(tokenPoolProxy); } @@ -106,7 +105,7 @@ abstract contract GhoBaseTest is BaseTest { // Deploy BurnMintTokenPool for GHO token on source chain UpgradeableBurnMintTokenPool tokenPoolImpl = new UpgradeableBurnMintTokenPool(ghoToken, arm, false); // proxy upgrade - vm.startPrank(proxyAdmin); + vm.prank(proxyAdmin); TransparentUpgradeableProxy(tokenPoolProxy).upgradeTo(address(tokenPoolImpl)); } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol index 6bea004e3f..c2314f887d 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol @@ -14,36 +14,374 @@ import {UpgradeableBurnMintTokenPool} from "../../../pools/GHO/UpgradeableBurnMi import {RateLimiter} from "../../../libraries/RateLimiter.sol"; import {GhoTokenPoolRemoteSetupOld} from "./GhoTokenPoolRemoteSetupOld.t.sol"; -contract GhoTokenPoolRemoteOld_setRateLimitAdmin is GhoTokenPoolRemoteSetupOld { - /*function testSetRateLimitAdminSuccess() public { - assertEq(address(0), s_pool.getRateLimitAdmin()); +contract GhoTokenPoolRemoteOld_lockOrBurn is GhoTokenPoolRemoteSetupOld { + function testSetupSuccess() public { + assertEq(address(s_burnMintERC677), address(s_pool.getToken())); + assertEq(address(s_mockARM), s_pool.getArmProxy()); + assertEq(false, s_pool.getAllowListEnabled()); + assertEq("BurnMintTokenPool 1.4.0", s_pool.typeAndVersion()); + } + + function testPoolBurnSuccess() public { + uint256 burnAmount = 20_000e18; + // inflate facilitator level + _inflateFacilitatorLevel(address(s_pool), address(s_burnMintERC677), burnAmount); + + deal(address(s_burnMintERC677), address(s_pool), burnAmount); + assertEq(s_burnMintERC677.balanceOf(address(s_pool)), burnAmount); + + vm.startPrank(s_burnMintOnRamp); + + vm.expectEmit(); + emit TokensConsumed(burnAmount); + + vm.expectEmit(); + emit Transfer(address(s_pool), address(0), burnAmount); + + vm.expectEmit(); + emit Burned(address(s_burnMintOnRamp), burnAmount); + + bytes4 expectedSignature = bytes4(keccak256("burn(uint256)")); + vm.expectCall(address(s_burnMintERC677), abi.encodeWithSelector(expectedSignature, burnAmount)); + + (uint256 preCapacity, uint256 preLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket(address(s_pool)); + + s_pool.lockOrBurn(OWNER, bytes(""), burnAmount, DEST_CHAIN_SELECTOR, bytes("")); + + // Facilitator checks + (uint256 postCapacity, uint256 postLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket( + address(s_pool) + ); + assertEq(postCapacity, preCapacity); + assertEq(preLevel - burnAmount, postLevel, "wrong facilitator bucket level"); + + assertEq(s_burnMintERC677.balanceOf(address(s_pool)), 0); + } + + // Should not burn tokens if cursed. + function testPoolBurnRevertNotHealthyReverts() public { + s_mockARM.voteToCurse(bytes32(0)); + uint256 before = s_burnMintERC677.balanceOf(address(s_pool)); + vm.startPrank(s_burnMintOnRamp); + + vm.expectRevert(EVM2EVMOnRamp.BadARMSignal.selector); + s_pool.lockOrBurn(OWNER, bytes(""), 1e5, DEST_CHAIN_SELECTOR, bytes("")); + + assertEq(s_burnMintERC677.balanceOf(address(s_pool)), before); + } + + function testChainNotAllowedReverts() public { + uint64 wrongChainSelector = 8838833; + vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ChainNotAllowed.selector, wrongChainSelector)); + s_pool.lockOrBurn(OWNER, bytes(""), 1, wrongChainSelector, bytes("")); + } + + function testPoolBurnNoPrivilegesReverts() public { + // Remove privileges + vm.startPrank(AAVE_DAO); + GhoToken(address(s_burnMintERC677)).removeFacilitator(address(s_pool)); + vm.stopPrank(); + + uint256 amount = 1; + vm.startPrank(s_burnMintOnRamp); + vm.expectRevert(stdError.arithmeticError); + s_pool.lockOrBurn(STRANGER, bytes(""), amount, DEST_CHAIN_SELECTOR, bytes("")); + } + + function testBucketLevelNotEnoughReverts() public { + (, uint256 bucketLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket(address(s_pool)); + assertEq(bucketLevel, 0); + + uint256 amount = 1; + vm.expectCall(address(s_burnMintERC677), abi.encodeWithSelector(GhoToken.burn.selector, amount)); + vm.expectRevert(stdError.arithmeticError); + vm.startPrank(s_burnMintOnRamp); + s_pool.lockOrBurn(STRANGER, bytes(""), amount, DEST_CHAIN_SELECTOR, bytes("")); + } + + function testTokenMaxCapacityExceededReverts() public { + RateLimiter.Config memory rateLimiterConfig = getOutboundRateLimiterConfig(); + uint256 capacity = rateLimiterConfig.capacity; + uint256 amount = 10 * capacity; + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_burnMintERC677)) + ); + vm.startPrank(s_burnMintOnRamp); + s_pool.lockOrBurn(STRANGER, bytes(""), amount, DEST_CHAIN_SELECTOR, bytes("")); + } +} + +contract GhoTokenPoolRemoteOld_releaseOrMint is GhoTokenPoolRemoteSetupOld { + function testPoolMintSuccess() public { + uint256 amount = 1e19; + vm.startPrank(s_burnMintOffRamp); + vm.expectEmit(); + emit Transfer(address(0), OWNER, amount); + s_pool.releaseOrMint(bytes(""), OWNER, amount, DEST_CHAIN_SELECTOR, bytes("")); + assertEq(s_burnMintERC677.balanceOf(OWNER), amount); + } + + function testPoolMintNotHealthyReverts() public { + // Should not mint tokens if cursed. + s_mockARM.voteToCurse(bytes32(0)); + uint256 before = s_burnMintERC677.balanceOf(OWNER); + vm.startPrank(s_burnMintOffRamp); + vm.expectRevert(EVM2EVMOffRamp.BadARMSignal.selector); + s_pool.releaseOrMint(bytes(""), OWNER, 1e5, DEST_CHAIN_SELECTOR, bytes("")); + assertEq(s_burnMintERC677.balanceOf(OWNER), before); + } + + function testChainNotAllowedReverts() public { + uint64 wrongChainSelector = 8838833; + vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ChainNotAllowed.selector, wrongChainSelector)); + s_pool.releaseOrMint(bytes(""), STRANGER, 1, wrongChainSelector, bytes("")); + } + + function testPoolMintNoPrivilegesReverts() public { + // Remove privileges + vm.startPrank(AAVE_DAO); + GhoToken(address(s_burnMintERC677)).removeFacilitator(address(s_pool)); + vm.stopPrank(); + + uint256 amount = 1; + vm.startPrank(s_burnMintOffRamp); + vm.expectRevert("FACILITATOR_BUCKET_CAPACITY_EXCEEDED"); + s_pool.releaseOrMint(bytes(""), STRANGER, amount, DEST_CHAIN_SELECTOR, bytes("")); + } + + function testBucketCapacityExceededReverts() public { + // Mint all the bucket capacity + (uint256 bucketCapacity, ) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket(address(s_pool)); + _inflateFacilitatorLevel(address(s_pool), address(s_burnMintERC677), bucketCapacity); + (uint256 currCapacity, uint256 currLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket( + address(s_pool) + ); + assertEq(currCapacity, currLevel); + + uint256 amount = 1; + vm.expectCall(address(s_burnMintERC677), abi.encodeWithSelector(GhoToken.mint.selector, STRANGER, amount)); + vm.expectRevert("FACILITATOR_BUCKET_CAPACITY_EXCEEDED"); + vm.startPrank(s_burnMintOffRamp); + s_pool.releaseOrMint(bytes(""), STRANGER, amount, DEST_CHAIN_SELECTOR, bytes("")); + } + + function testTokenMaxCapacityExceededReverts() public { + RateLimiter.Config memory rateLimiterConfig = getInboundRateLimiterConfig(); + uint256 capacity = rateLimiterConfig.capacity; + uint256 amount = 10 * capacity; + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_burnMintERC677)) + ); + vm.startPrank(s_burnMintOffRamp); + s_pool.releaseOrMint(bytes(""), STRANGER, amount, DEST_CHAIN_SELECTOR, bytes("")); + } +} + +contract GhoTokenPoolOldEthereum_upgradeability is GhoTokenPoolRemoteSetupOld { + function testInitialization() public { + // Upgradeability + assertEq(_getUpgradeableVersion(address(s_pool)), 1); + vm.startPrank(PROXY_ADMIN); + (bool ok, bytes memory result) = address(s_pool).staticcall( + abi.encodeWithSelector(TransparentUpgradeableProxy.admin.selector) + ); + assertTrue(ok, "proxy admin fetch failed"); + address decodedProxyAdmin = abi.decode(result, (address)); + assertEq(decodedProxyAdmin, PROXY_ADMIN, "proxy admin is wrong"); + assertEq(decodedProxyAdmin, _getProxyAdminAddress(address(s_pool)), "proxy admin is wrong"); + + // TokenPool + vm.startPrank(OWNER); + assertEq(s_pool.getAllowList().length, 0); + assertEq(s_pool.getAllowListEnabled(), false); + assertEq(s_pool.getArmProxy(), address(s_mockARM)); + assertEq(s_pool.getRouter(), address(s_sourceRouter)); + assertEq(address(s_pool.getToken()), address(s_burnMintERC677)); + assertEq(s_pool.owner(), AAVE_DAO, "owner is wrong"); + } + + function testUpgrade() public { + MockUpgradeable newImpl = new MockUpgradeable(); + bytes memory mockImpleParams = abi.encodeWithSignature("initialize()"); + vm.startPrank(PROXY_ADMIN); + TransparentUpgradeableProxy(payable(address(s_pool))).upgradeToAndCall(address(newImpl), mockImpleParams); + + vm.startPrank(OWNER); + assertEq(_getUpgradeableVersion(address(s_pool)), 2); + } + + function testUpgradeAdminReverts() public { + vm.expectRevert(); + TransparentUpgradeableProxy(payable(address(s_pool))).upgradeToAndCall(address(0), bytes("")); + assertEq(_getUpgradeableVersion(address(s_pool)), 1); + + vm.expectRevert(); + TransparentUpgradeableProxy(payable(address(s_pool))).upgradeTo(address(0)); + assertEq(_getUpgradeableVersion(address(s_pool)), 1); + } + + function testChangeAdmin() public { + assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN); + + address newAdmin = makeAddr("newAdmin"); + vm.startPrank(PROXY_ADMIN); + TransparentUpgradeableProxy(payable(address(s_pool))).changeAdmin(newAdmin); + + assertEq(_getProxyAdminAddress(address(s_pool)), newAdmin, "Admin change failed"); + } + + function testChangeAdminAdminReverts() public { + assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN); + + address newAdmin = makeAddr("newAdmin"); + vm.expectRevert(); + TransparentUpgradeableProxy(payable(address(s_pool))).changeAdmin(newAdmin); + + assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN, "Unauthorized admin change"); + } +} + +contract GhoTokenPoolRemoteOld_setChainRateLimiterConfig is GhoTokenPoolRemoteSetupOld { + event ConfigChanged(RateLimiter.Config); + event ChainConfigured( + uint64 chainSelector, + RateLimiter.Config outboundRateLimiterConfig, + RateLimiter.Config inboundRateLimiterConfig + ); + + uint64 internal s_remoteChainSelector; + + function setUp() public virtual override { + GhoTokenPoolRemoteSetupOld.setUp(); + UpgradeableTokenPool.ChainUpdate[] memory chainUpdates = new UpgradeableTokenPool.ChainUpdate[](1); + s_remoteChainSelector = 123124; + chainUpdates[0] = UpgradeableTokenPool.ChainUpdate({ + remoteChainSelector: s_remoteChainSelector, + allowed: true, + outboundRateLimiterConfig: getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: getInboundRateLimiterConfig() + }); changePrank(AAVE_DAO); - s_pool.setRateLimitAdmin(OWNER); - assertEq(OWNER, s_pool.getRateLimitAdmin()); - }*/ + s_pool.applyChainUpdates(chainUpdates); + changePrank(OWNER); + } - // Reverts + function testFuzz_SetChainRateLimiterConfigSuccess(uint128 capacity, uint128 rate, uint32 newTime) public { + // Cap the lower bound to 4 so 4/2 is still >= 2 + vm.assume(capacity >= 4); + // Cap the lower bound to 2 so 2/2 is still >= 1 + rate = uint128(bound(rate, 2, capacity - 2)); + // Bucket updates only work on increasing time + newTime = uint32(bound(newTime, block.timestamp + 1, type(uint32).max)); + vm.warp(newTime); + + uint256 oldOutboundTokens = s_pool.getCurrentOutboundRateLimiterState(s_remoteChainSelector).tokens; + uint256 oldInboundTokens = s_pool.getCurrentInboundRateLimiterState(s_remoteChainSelector).tokens; + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({isEnabled: true, capacity: capacity, rate: rate}); + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: true, + capacity: capacity / 2, + rate: rate / 2 + }); + + vm.expectEmit(); + emit ConfigChanged(newOutboundConfig); + vm.expectEmit(); + emit ConfigChanged(newInboundConfig); + vm.expectEmit(); + emit ChainConfigured(s_remoteChainSelector, newOutboundConfig, newInboundConfig); - // Should fail because old implementation does not have rate limiter - function testSetRateLimitRevert() public { changePrank(AAVE_DAO); - vm.expectRevert(); - s_pool.setRateLimitAdmin(OWNER); + s_pool.setChainRateLimiterConfig(s_remoteChainSelector, newOutboundConfig, newInboundConfig); + + uint256 expectedTokens = RateLimiter._min(newOutboundConfig.capacity, oldOutboundTokens); + + RateLimiter.TokenBucket memory bucket = s_pool.getCurrentOutboundRateLimiterState(s_remoteChainSelector); + assertEq(bucket.capacity, newOutboundConfig.capacity); + assertEq(bucket.rate, newOutboundConfig.rate); + assertEq(bucket.tokens, expectedTokens); + assertEq(bucket.lastUpdated, newTime); + + expectedTokens = RateLimiter._min(newInboundConfig.capacity, oldInboundTokens); + + bucket = s_pool.getCurrentInboundRateLimiterState(s_remoteChainSelector); + assertEq(bucket.capacity, newInboundConfig.capacity); + assertEq(bucket.rate, newInboundConfig.rate); + assertEq(bucket.tokens, expectedTokens); + assertEq(bucket.lastUpdated, newTime); } - function testSetRateLimitAfterUpgrade() public { - _upgradeUpgradeableBurnMintTokenPool(payable(address(s_pool)), address(s_burnMintERC677), ARM_PROXY, PROXY_ADMIN); + function testOnlyOwnerOrRateLimitAdminSuccess() public { + address rateLimiterAdmin = address(28973509103597907); + changePrank(AAVE_DAO); + s_pool.setRateLimitAdmin(rateLimiterAdmin); + + changePrank(rateLimiterAdmin); + + s_pool.setChainRateLimiterConfig( + s_remoteChainSelector, + getOutboundRateLimiterConfig(), + getInboundRateLimiterConfig() + ); + + changePrank(AAVE_DAO); + + s_pool.setChainRateLimiterConfig( + s_remoteChainSelector, + getOutboundRateLimiterConfig(), + getInboundRateLimiterConfig() + ); + } + + // Reverts + + function testOnlyOwnerReverts() public { + changePrank(STRANGER); + + vm.expectRevert(abi.encodeWithSelector(UpgradeableBurnMintTokenPool.Unauthorized.selector, STRANGER)); + s_pool.setChainRateLimiterConfig( + s_remoteChainSelector, + getOutboundRateLimiterConfig(), + getInboundRateLimiterConfig() + ); + } + + function testNonExistentChainReverts() public { + uint64 wrongChainSelector = 9084102894; + + vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.NonExistentChain.selector, wrongChainSelector)); + changePrank(AAVE_DAO); + s_pool.setChainRateLimiterConfig(wrongChainSelector, getOutboundRateLimiterConfig(), getInboundRateLimiterConfig()); + } +} + +contract GhoTokenPoolRemoteOld_setRateLimitAdmin is GhoTokenPoolRemoteSetupOld { + function testSetRateLimitAdminSuccess() public { + assertEq(address(0), s_pool.getRateLimitAdmin()); + vm.startPrank(AAVE_DAO); s_pool.setRateLimitAdmin(OWNER); assertEq(OWNER, s_pool.getRateLimitAdmin()); + vm.stopPrank(); + } + + // Reverts + + function testSetRateLimitRevertBeforeUpgrade() public { + _deployWithoutUpgrade(); + vm.startPrank(AAVE_DAO); + vm.expectRevert(); + s_pool.setRateLimitAdmin(OWNER); + vm.stopPrank(); } - /* function testSetRateLimitAdminReverts() public { vm.startPrank(STRANGER); vm.expectRevert("Only callable by owner"); s_pool.setRateLimitAdmin(STRANGER); } - */ } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol index 784481b362..753849611f 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol @@ -42,6 +42,37 @@ contract GhoTokenPoolRemoteSetupOld is RouterSetup, GhoBaseTest { ) ); + // Upgrade the old pool after deployment + _upgradeUpgradeableBurnMintTokenPool( + payable(address(s_pool)), + address(s_burnMintERC677), + address(s_mockARM), + PROXY_ADMIN + ); + + // Give mint and burn privileges to source UpgradeableTokenPool (GHO-specific related) + vm.startPrank(AAVE_DAO); + GhoToken(address(s_burnMintERC677)).grantRole( + GhoToken(address(s_burnMintERC677)).FACILITATOR_MANAGER_ROLE(), + AAVE_DAO + ); + GhoToken(address(s_burnMintERC677)).addFacilitator(address(s_pool), "UpgradeableTokenPool", type(uint128).max); + vm.stopPrank(); + + _applyChainUpdates(address(s_pool)); + } + + function _deployWithoutUpgrade() internal { + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( + address(s_burnMintERC677), + address(s_mockARM), + address(s_sourceRouter), + AAVE_DAO, + PROXY_ADMIN + ) + ); + // Give mint and burn privileges to source UpgradeableTokenPool (GHO-specific related) vm.stopPrank(); vm.startPrank(AAVE_DAO); @@ -74,5 +105,6 @@ contract GhoTokenPoolRemoteSetupOld is RouterSetup, GhoBaseTest { Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); offRampUpdates[0] = Router.OffRamp({sourceChainSelector: DEST_CHAIN_SELECTOR, offRamp: s_burnMintOffRamp}); s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + vm.stopPrank(); } } From 37aebc939335cfdb1e4afd6076324ca0116455cd Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Wed, 17 Jul 2024 12:44:35 -0700 Subject: [PATCH 07/53] feat: reserve storage space for future upgrades --- .../src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index a46ff915e5..450222258d 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -27,6 +27,9 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken /// @dev Can be address(0) if none is configured. address internal s_rateLimitAdmin; + // Reserved storage space to allow for layout changes in the future. + uint256[50] private __gap; + /// @dev Constructor /// @param token The bridgeable token that is managed by this pool. /// @param armProxy The address of the arm proxy From 68e504baeb2b3ec9a02927973e72e46f32f9efe0 Mon Sep 17 00:00:00 2001 From: miguelmtzinf Date: Thu, 18 Jul 2024 09:23:07 +0200 Subject: [PATCH 08/53] diff: Update diff for UpgradeableBurnMintTokenPool --- .../UpgradeableBurnMintTokenPool_diff.md | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index 05c3be4d06..066847e4f8 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -index 9af0f22f4c..58be87812f 100644 +index 9af0f22f4c..a46ff915e5 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -@@ -1,28 +1,55 @@ +@@ -1,28 +1,90 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; @@ -16,15 +16,10 @@ index 9af0f22f4c..58be87812f 100644 -import {BurnMintTokenPoolAbstract} from "./BurnMintTokenPoolAbstract.sol"; +import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; +import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; - --/// @notice This pool mints and burns a 3rd-party token. --/// @dev Pool whitelisting mode is set in the constructor and cannot be modified later. --/// It either accepts any address as originalSender, or only accepts whitelisted originalSender. --/// The only way to change whitelisting mode is to deploy a new pool. --/// If that is expected, please make sure the token's burner/minter roles are adjustable. --contract BurnMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion { ++ +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; +import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; ++import {RateLimiter} from "../../libraries/RateLimiter.sol"; + +import {IRouter} from "../../interfaces/IRouter.sol"; + @@ -35,8 +30,20 @@ index 9af0f22f4c..58be87812f 100644 +/// - Implementation of Initializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage +contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { ++ error Unauthorized(address caller); + +-/// @notice This pool mints and burns a 3rd-party token. +-/// @dev Pool whitelisting mode is set in the constructor and cannot be modified later. +-/// It either accepts any address as originalSender, or only accepts whitelisted originalSender. +-/// The only way to change whitelisting mode is to deploy a new pool. +-/// If that is expected, please make sure the token's burner/minter roles are adjustable. +-contract BurnMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion { string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; ++ /// @notice The address of the rate limiter admin. ++ /// @dev Can be address(0) if none is configured. ++ address internal s_rateLimitAdmin; ++ + /// @dev Constructor + /// @param token The bridgeable token that is managed by this pool. + /// @param armProxy The address of the arm proxy @@ -71,6 +78,34 @@ index 9af0f22f4c..58be87812f 100644 + } + } + ++ /// @notice Sets the rate limiter admin address. ++ /// @dev Only callable by the owner. ++ /// @param rateLimitAdmin The new rate limiter admin address. ++ function setRateLimitAdmin(address rateLimitAdmin) external onlyOwner { ++ s_rateLimitAdmin = rateLimitAdmin; ++ } ++ ++ /// @notice Gets the rate limiter admin address. ++ function getRateLimitAdmin() external view returns (address) { ++ return s_rateLimitAdmin; ++ } ++ ++ /// @notice Sets the rate limiter admin address. ++ /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal ++ /// onlyAdmin check in the base implementation to also allow the rate limiter admin. ++ /// @param remoteChainSelector The remote chain selector for which the rate limits apply. ++ /// @param outboundConfig The new outbound rate limiter config. ++ /// @param inboundConfig The new inbound rate limiter config. ++ function setChainRateLimiterConfig( ++ uint64 remoteChainSelector, ++ RateLimiter.Config memory outboundConfig, ++ RateLimiter.Config memory inboundConfig ++ ) external override { ++ if (msg.sender != s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); ++ ++ _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); ++ } ++ + /// @inheritdoc UpgradeableBurnMintTokenPoolAbstract function _burn(uint256 amount) internal virtual override { IBurnMintERC20(address(i_token)).burn(amount); From 393649d9e40d3ed418ac9e87c47f1cc901242d8d Mon Sep 17 00:00:00 2001 From: miguelmtzinf Date: Thu, 18 Jul 2024 09:32:27 +0200 Subject: [PATCH 09/53] ci: Fix ci --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f535d2ef2..ae5c6f11b8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: product: [ccip] name: Foundry Tests ${{ matrix.product }} # See https://github.com/foundry-rs/foundry/issues/3827 - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest # The if statements for steps after checkout repo is workaround for # passing required check for PRs that don't have filtered changes. From 3ddfc853660e9f71e0a2873e75bc7313228eb163 Mon Sep 17 00:00:00 2001 From: miguelmtzinf Date: Thu, 18 Jul 2024 10:35:41 +0200 Subject: [PATCH 10/53] ci: Fix ci --- .github/actions/setup-nodejs/action.yaml | 8 ++++---- .github/workflows/tests.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-nodejs/action.yaml b/.github/actions/setup-nodejs/action.yaml index da605ac769..8e20784341 100644 --- a/.github/actions/setup-nodejs/action.yaml +++ b/.github/actions/setup-nodejs/action.yaml @@ -7,13 +7,13 @@ description: Setup pnpm for contracts runs: using: composite steps: - - uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd # v2.2.4 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 with: - version: ^7.0.0 + version: ^9.0.0 - - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: - node-version: "16" + node-version: "20" cache: "pnpm" cache-dependency-path: "contracts/pnpm-lock.yaml" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ae5c6f11b8..5f535d2ef2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: product: [ccip] name: Foundry Tests ${{ matrix.product }} # See https://github.com/foundry-rs/foundry/issues/3827 - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 # The if statements for steps after checkout repo is workaround for # passing required check for PRs that don't have filtered changes. From 7af804e2b1982f54430f92e7483a369b8a6da7a1 Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Thu, 18 Jul 2024 10:09:55 -0700 Subject: [PATCH 11/53] test: show init reverting after upgrade --- .../v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol index c2314f887d..97df9a823e 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol @@ -241,6 +241,12 @@ contract GhoTokenPoolOldEthereum_upgradeability is GhoTokenPoolRemoteSetupOld { assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN, "Unauthorized admin change"); } + + function testCallInitReverts() public { + vm.startPrank(OWNER); + vm.expectRevert("Initializable: contract is already initialized"); + s_pool.initialize(OWNER, new address[](0), address(s_sourceRouter)); + } } contract GhoTokenPoolRemoteOld_setChainRateLimiterConfig is GhoTokenPoolRemoteSetupOld { From f879d82ebc9b8034323b2c47a0f22fc9ff08213e Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Fri, 19 Jul 2024 14:38:57 -0700 Subject: [PATCH 12/53] fix: remove gap --- .../src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 450222258d..cbfb88567d 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -27,9 +27,6 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken /// @dev Can be address(0) if none is configured. address internal s_rateLimitAdmin; - // Reserved storage space to allow for layout changes in the future. - uint256[50] private __gap; - /// @dev Constructor /// @param token The bridgeable token that is managed by this pool. /// @param armProxy The address of the arm proxy @@ -91,4 +88,4 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken function _burn(uint256 amount) internal virtual override { IBurnMintERC20(address(i_token)).burn(amount); } -} +} \ No newline at end of file From b7d9f6deb4646016970e6cd6649e061ac9865d0f Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Fri, 19 Jul 2024 14:56:08 -0700 Subject: [PATCH 13/53] fix: return to original test suite --- .../ccip/test/pools/GHO/GhoBaseTest.t.sol | 46 +- .../pools/GHO/GhoTokenPoolRemoteOld.t.sol | 393 ------------------ .../GHO/GhoTokenPoolRemoteSetupOld.t.sol | 110 ----- 3 files changed, 1 insertion(+), 548 deletions(-) delete mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol delete mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol 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 b1721855b4..8f1b66d216 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,7 +8,6 @@ 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"; @@ -66,49 +65,6 @@ 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 _upgradeUpgradeableBurnMintTokenPool( - address payable tokenPoolProxy, - address ghoToken, - address arm, - address proxyAdmin - ) internal { - // Deploy BurnMintTokenPool for GHO token on source chain - UpgradeableBurnMintTokenPool tokenPoolImpl = new UpgradeableBurnMintTokenPool(ghoToken, arm, false); - // proxy upgrade - vm.prank(proxyAdmin); - TransparentUpgradeableProxy(tokenPoolProxy).upgradeTo(address(tokenPoolImpl)); - } - function _deployUpgradeableLockReleaseTokenPool( address ghoToken, address arm, @@ -323,4 +279,4 @@ abstract contract GhoBaseTest is BaseTest { function _isEthereumChain(uint256 chainId) internal pure returns (bool) { return chainId == 0; } -} +} \ No newline at end of file diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol deleted file mode 100644 index 97df9a823e..0000000000 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteOld.t.sol +++ /dev/null @@ -1,393 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; - -import {GhoToken} from "@aave/gho-core/gho/GhoToken.sol"; -import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; - -import {stdError} from "forge-std/Test.sol"; -import {MockUpgradeable} from "../../mocks/MockUpgradeable.sol"; -import {UpgradeableTokenPool} from "../../../pools/GHO/UpgradeableTokenPool.sol"; -import {EVM2EVMOnRamp} from "../../../onRamp/EVM2EVMOnRamp.sol"; -import {EVM2EVMOffRamp} from "../../../offRamp/EVM2EVMOffRamp.sol"; -import {BurnMintTokenPool} from "../../../pools/BurnMintTokenPool.sol"; -import {UpgradeableBurnMintTokenPool} from "../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; -import {RateLimiter} from "../../../libraries/RateLimiter.sol"; -import {GhoTokenPoolRemoteSetupOld} from "./GhoTokenPoolRemoteSetupOld.t.sol"; - -contract GhoTokenPoolRemoteOld_lockOrBurn is GhoTokenPoolRemoteSetupOld { - function testSetupSuccess() public { - assertEq(address(s_burnMintERC677), address(s_pool.getToken())); - assertEq(address(s_mockARM), s_pool.getArmProxy()); - assertEq(false, s_pool.getAllowListEnabled()); - assertEq("BurnMintTokenPool 1.4.0", s_pool.typeAndVersion()); - } - - function testPoolBurnSuccess() public { - uint256 burnAmount = 20_000e18; - // inflate facilitator level - _inflateFacilitatorLevel(address(s_pool), address(s_burnMintERC677), burnAmount); - - deal(address(s_burnMintERC677), address(s_pool), burnAmount); - assertEq(s_burnMintERC677.balanceOf(address(s_pool)), burnAmount); - - vm.startPrank(s_burnMintOnRamp); - - vm.expectEmit(); - emit TokensConsumed(burnAmount); - - vm.expectEmit(); - emit Transfer(address(s_pool), address(0), burnAmount); - - vm.expectEmit(); - emit Burned(address(s_burnMintOnRamp), burnAmount); - - bytes4 expectedSignature = bytes4(keccak256("burn(uint256)")); - vm.expectCall(address(s_burnMintERC677), abi.encodeWithSelector(expectedSignature, burnAmount)); - - (uint256 preCapacity, uint256 preLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket(address(s_pool)); - - s_pool.lockOrBurn(OWNER, bytes(""), burnAmount, DEST_CHAIN_SELECTOR, bytes("")); - - // Facilitator checks - (uint256 postCapacity, uint256 postLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket( - address(s_pool) - ); - assertEq(postCapacity, preCapacity); - assertEq(preLevel - burnAmount, postLevel, "wrong facilitator bucket level"); - - assertEq(s_burnMintERC677.balanceOf(address(s_pool)), 0); - } - - // Should not burn tokens if cursed. - function testPoolBurnRevertNotHealthyReverts() public { - s_mockARM.voteToCurse(bytes32(0)); - uint256 before = s_burnMintERC677.balanceOf(address(s_pool)); - vm.startPrank(s_burnMintOnRamp); - - vm.expectRevert(EVM2EVMOnRamp.BadARMSignal.selector); - s_pool.lockOrBurn(OWNER, bytes(""), 1e5, DEST_CHAIN_SELECTOR, bytes("")); - - assertEq(s_burnMintERC677.balanceOf(address(s_pool)), before); - } - - function testChainNotAllowedReverts() public { - uint64 wrongChainSelector = 8838833; - vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ChainNotAllowed.selector, wrongChainSelector)); - s_pool.lockOrBurn(OWNER, bytes(""), 1, wrongChainSelector, bytes("")); - } - - function testPoolBurnNoPrivilegesReverts() public { - // Remove privileges - vm.startPrank(AAVE_DAO); - GhoToken(address(s_burnMintERC677)).removeFacilitator(address(s_pool)); - vm.stopPrank(); - - uint256 amount = 1; - vm.startPrank(s_burnMintOnRamp); - vm.expectRevert(stdError.arithmeticError); - s_pool.lockOrBurn(STRANGER, bytes(""), amount, DEST_CHAIN_SELECTOR, bytes("")); - } - - function testBucketLevelNotEnoughReverts() public { - (, uint256 bucketLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket(address(s_pool)); - assertEq(bucketLevel, 0); - - uint256 amount = 1; - vm.expectCall(address(s_burnMintERC677), abi.encodeWithSelector(GhoToken.burn.selector, amount)); - vm.expectRevert(stdError.arithmeticError); - vm.startPrank(s_burnMintOnRamp); - s_pool.lockOrBurn(STRANGER, bytes(""), amount, DEST_CHAIN_SELECTOR, bytes("")); - } - - function testTokenMaxCapacityExceededReverts() public { - RateLimiter.Config memory rateLimiterConfig = getOutboundRateLimiterConfig(); - uint256 capacity = rateLimiterConfig.capacity; - uint256 amount = 10 * capacity; - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_burnMintERC677)) - ); - vm.startPrank(s_burnMintOnRamp); - s_pool.lockOrBurn(STRANGER, bytes(""), amount, DEST_CHAIN_SELECTOR, bytes("")); - } -} - -contract GhoTokenPoolRemoteOld_releaseOrMint is GhoTokenPoolRemoteSetupOld { - function testPoolMintSuccess() public { - uint256 amount = 1e19; - vm.startPrank(s_burnMintOffRamp); - vm.expectEmit(); - emit Transfer(address(0), OWNER, amount); - s_pool.releaseOrMint(bytes(""), OWNER, amount, DEST_CHAIN_SELECTOR, bytes("")); - assertEq(s_burnMintERC677.balanceOf(OWNER), amount); - } - - function testPoolMintNotHealthyReverts() public { - // Should not mint tokens if cursed. - s_mockARM.voteToCurse(bytes32(0)); - uint256 before = s_burnMintERC677.balanceOf(OWNER); - vm.startPrank(s_burnMintOffRamp); - vm.expectRevert(EVM2EVMOffRamp.BadARMSignal.selector); - s_pool.releaseOrMint(bytes(""), OWNER, 1e5, DEST_CHAIN_SELECTOR, bytes("")); - assertEq(s_burnMintERC677.balanceOf(OWNER), before); - } - - function testChainNotAllowedReverts() public { - uint64 wrongChainSelector = 8838833; - vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ChainNotAllowed.selector, wrongChainSelector)); - s_pool.releaseOrMint(bytes(""), STRANGER, 1, wrongChainSelector, bytes("")); - } - - function testPoolMintNoPrivilegesReverts() public { - // Remove privileges - vm.startPrank(AAVE_DAO); - GhoToken(address(s_burnMintERC677)).removeFacilitator(address(s_pool)); - vm.stopPrank(); - - uint256 amount = 1; - vm.startPrank(s_burnMintOffRamp); - vm.expectRevert("FACILITATOR_BUCKET_CAPACITY_EXCEEDED"); - s_pool.releaseOrMint(bytes(""), STRANGER, amount, DEST_CHAIN_SELECTOR, bytes("")); - } - - function testBucketCapacityExceededReverts() public { - // Mint all the bucket capacity - (uint256 bucketCapacity, ) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket(address(s_pool)); - _inflateFacilitatorLevel(address(s_pool), address(s_burnMintERC677), bucketCapacity); - (uint256 currCapacity, uint256 currLevel) = GhoToken(address(s_burnMintERC677)).getFacilitatorBucket( - address(s_pool) - ); - assertEq(currCapacity, currLevel); - - uint256 amount = 1; - vm.expectCall(address(s_burnMintERC677), abi.encodeWithSelector(GhoToken.mint.selector, STRANGER, amount)); - vm.expectRevert("FACILITATOR_BUCKET_CAPACITY_EXCEEDED"); - vm.startPrank(s_burnMintOffRamp); - s_pool.releaseOrMint(bytes(""), STRANGER, amount, DEST_CHAIN_SELECTOR, bytes("")); - } - - function testTokenMaxCapacityExceededReverts() public { - RateLimiter.Config memory rateLimiterConfig = getInboundRateLimiterConfig(); - uint256 capacity = rateLimiterConfig.capacity; - uint256 amount = 10 * capacity; - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_burnMintERC677)) - ); - vm.startPrank(s_burnMintOffRamp); - s_pool.releaseOrMint(bytes(""), STRANGER, amount, DEST_CHAIN_SELECTOR, bytes("")); - } -} - -contract GhoTokenPoolOldEthereum_upgradeability is GhoTokenPoolRemoteSetupOld { - function testInitialization() public { - // Upgradeability - assertEq(_getUpgradeableVersion(address(s_pool)), 1); - vm.startPrank(PROXY_ADMIN); - (bool ok, bytes memory result) = address(s_pool).staticcall( - abi.encodeWithSelector(TransparentUpgradeableProxy.admin.selector) - ); - assertTrue(ok, "proxy admin fetch failed"); - address decodedProxyAdmin = abi.decode(result, (address)); - assertEq(decodedProxyAdmin, PROXY_ADMIN, "proxy admin is wrong"); - assertEq(decodedProxyAdmin, _getProxyAdminAddress(address(s_pool)), "proxy admin is wrong"); - - // TokenPool - vm.startPrank(OWNER); - assertEq(s_pool.getAllowList().length, 0); - assertEq(s_pool.getAllowListEnabled(), false); - assertEq(s_pool.getArmProxy(), address(s_mockARM)); - assertEq(s_pool.getRouter(), address(s_sourceRouter)); - assertEq(address(s_pool.getToken()), address(s_burnMintERC677)); - assertEq(s_pool.owner(), AAVE_DAO, "owner is wrong"); - } - - function testUpgrade() public { - MockUpgradeable newImpl = new MockUpgradeable(); - bytes memory mockImpleParams = abi.encodeWithSignature("initialize()"); - vm.startPrank(PROXY_ADMIN); - TransparentUpgradeableProxy(payable(address(s_pool))).upgradeToAndCall(address(newImpl), mockImpleParams); - - vm.startPrank(OWNER); - assertEq(_getUpgradeableVersion(address(s_pool)), 2); - } - - function testUpgradeAdminReverts() public { - vm.expectRevert(); - TransparentUpgradeableProxy(payable(address(s_pool))).upgradeToAndCall(address(0), bytes("")); - assertEq(_getUpgradeableVersion(address(s_pool)), 1); - - vm.expectRevert(); - TransparentUpgradeableProxy(payable(address(s_pool))).upgradeTo(address(0)); - assertEq(_getUpgradeableVersion(address(s_pool)), 1); - } - - function testChangeAdmin() public { - assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN); - - address newAdmin = makeAddr("newAdmin"); - vm.startPrank(PROXY_ADMIN); - TransparentUpgradeableProxy(payable(address(s_pool))).changeAdmin(newAdmin); - - assertEq(_getProxyAdminAddress(address(s_pool)), newAdmin, "Admin change failed"); - } - - function testChangeAdminAdminReverts() public { - assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN); - - address newAdmin = makeAddr("newAdmin"); - vm.expectRevert(); - TransparentUpgradeableProxy(payable(address(s_pool))).changeAdmin(newAdmin); - - assertEq(_getProxyAdminAddress(address(s_pool)), PROXY_ADMIN, "Unauthorized admin change"); - } - - function testCallInitReverts() public { - vm.startPrank(OWNER); - vm.expectRevert("Initializable: contract is already initialized"); - s_pool.initialize(OWNER, new address[](0), address(s_sourceRouter)); - } -} - -contract GhoTokenPoolRemoteOld_setChainRateLimiterConfig is GhoTokenPoolRemoteSetupOld { - event ConfigChanged(RateLimiter.Config); - event ChainConfigured( - uint64 chainSelector, - RateLimiter.Config outboundRateLimiterConfig, - RateLimiter.Config inboundRateLimiterConfig - ); - - uint64 internal s_remoteChainSelector; - - function setUp() public virtual override { - GhoTokenPoolRemoteSetupOld.setUp(); - UpgradeableTokenPool.ChainUpdate[] memory chainUpdates = new UpgradeableTokenPool.ChainUpdate[](1); - s_remoteChainSelector = 123124; - chainUpdates[0] = UpgradeableTokenPool.ChainUpdate({ - remoteChainSelector: s_remoteChainSelector, - allowed: true, - outboundRateLimiterConfig: getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: getInboundRateLimiterConfig() - }); - changePrank(AAVE_DAO); - s_pool.applyChainUpdates(chainUpdates); - changePrank(OWNER); - } - - function testFuzz_SetChainRateLimiterConfigSuccess(uint128 capacity, uint128 rate, uint32 newTime) public { - // Cap the lower bound to 4 so 4/2 is still >= 2 - vm.assume(capacity >= 4); - // Cap the lower bound to 2 so 2/2 is still >= 1 - rate = uint128(bound(rate, 2, capacity - 2)); - // Bucket updates only work on increasing time - newTime = uint32(bound(newTime, block.timestamp + 1, type(uint32).max)); - vm.warp(newTime); - - uint256 oldOutboundTokens = s_pool.getCurrentOutboundRateLimiterState(s_remoteChainSelector).tokens; - uint256 oldInboundTokens = s_pool.getCurrentInboundRateLimiterState(s_remoteChainSelector).tokens; - - RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({isEnabled: true, capacity: capacity, rate: rate}); - RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ - isEnabled: true, - capacity: capacity / 2, - rate: rate / 2 - }); - - vm.expectEmit(); - emit ConfigChanged(newOutboundConfig); - vm.expectEmit(); - emit ConfigChanged(newInboundConfig); - vm.expectEmit(); - emit ChainConfigured(s_remoteChainSelector, newOutboundConfig, newInboundConfig); - - changePrank(AAVE_DAO); - s_pool.setChainRateLimiterConfig(s_remoteChainSelector, newOutboundConfig, newInboundConfig); - - uint256 expectedTokens = RateLimiter._min(newOutboundConfig.capacity, oldOutboundTokens); - - RateLimiter.TokenBucket memory bucket = s_pool.getCurrentOutboundRateLimiterState(s_remoteChainSelector); - assertEq(bucket.capacity, newOutboundConfig.capacity); - assertEq(bucket.rate, newOutboundConfig.rate); - assertEq(bucket.tokens, expectedTokens); - assertEq(bucket.lastUpdated, newTime); - - expectedTokens = RateLimiter._min(newInboundConfig.capacity, oldInboundTokens); - - bucket = s_pool.getCurrentInboundRateLimiterState(s_remoteChainSelector); - assertEq(bucket.capacity, newInboundConfig.capacity); - assertEq(bucket.rate, newInboundConfig.rate); - assertEq(bucket.tokens, expectedTokens); - assertEq(bucket.lastUpdated, newTime); - } - - function testOnlyOwnerOrRateLimitAdminSuccess() public { - address rateLimiterAdmin = address(28973509103597907); - - changePrank(AAVE_DAO); - s_pool.setRateLimitAdmin(rateLimiterAdmin); - - changePrank(rateLimiterAdmin); - - s_pool.setChainRateLimiterConfig( - s_remoteChainSelector, - getOutboundRateLimiterConfig(), - getInboundRateLimiterConfig() - ); - - changePrank(AAVE_DAO); - - s_pool.setChainRateLimiterConfig( - s_remoteChainSelector, - getOutboundRateLimiterConfig(), - getInboundRateLimiterConfig() - ); - } - - // Reverts - - function testOnlyOwnerReverts() public { - changePrank(STRANGER); - - vm.expectRevert(abi.encodeWithSelector(UpgradeableBurnMintTokenPool.Unauthorized.selector, STRANGER)); - s_pool.setChainRateLimiterConfig( - s_remoteChainSelector, - getOutboundRateLimiterConfig(), - getInboundRateLimiterConfig() - ); - } - - function testNonExistentChainReverts() public { - uint64 wrongChainSelector = 9084102894; - - vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.NonExistentChain.selector, wrongChainSelector)); - changePrank(AAVE_DAO); - s_pool.setChainRateLimiterConfig(wrongChainSelector, getOutboundRateLimiterConfig(), getInboundRateLimiterConfig()); - } -} - -contract GhoTokenPoolRemoteOld_setRateLimitAdmin is GhoTokenPoolRemoteSetupOld { - function testSetRateLimitAdminSuccess() public { - assertEq(address(0), s_pool.getRateLimitAdmin()); - vm.startPrank(AAVE_DAO); - s_pool.setRateLimitAdmin(OWNER); - assertEq(OWNER, s_pool.getRateLimitAdmin()); - vm.stopPrank(); - } - - // Reverts - - function testSetRateLimitRevertBeforeUpgrade() public { - _deployWithoutUpgrade(); - vm.startPrank(AAVE_DAO); - vm.expectRevert(); - s_pool.setRateLimitAdmin(OWNER); - vm.stopPrank(); - } - - function testSetRateLimitAdminReverts() public { - vm.startPrank(STRANGER); - - vm.expectRevert("Only callable by owner"); - s_pool.setRateLimitAdmin(STRANGER); - } -} diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol deleted file mode 100644 index 753849611f..0000000000 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteSetupOld.t.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; - -import {GhoToken} from "@aave/gho-core/gho/GhoToken.sol"; -import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; - -import {stdError} from "forge-std/Test.sol"; -import {UpgradeableTokenPool} from "../../../pools/GHO/UpgradeableTokenPool.sol"; -import {Router} from "../../../Router.sol"; -import {BurnMintERC677} from "../../../../shared/token/ERC677/BurnMintERC677.sol"; -import {UpgradeableBurnMintTokenPoolOld} from "../../../pools/GHO/UpgradeableBurnMintTokenPoolOld.sol"; -import {UpgradeableBurnMintTokenPool} from "../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; -import {RouterSetup} from "../../router/RouterSetup.t.sol"; -import {BaseTest} from "../../BaseTest.t.sol"; -import {GhoBaseTest} from "./GhoBaseTest.t.sol"; - -contract GhoTokenPoolRemoteSetupOld is RouterSetup, GhoBaseTest { - event Transfer(address indexed from, address indexed to, uint256 value); - event TokensConsumed(uint256 tokens); - event Burned(address indexed sender, uint256 amount); - - BurnMintERC677 internal s_burnMintERC677; - address internal s_burnMintOffRamp = makeAddr("burn_mint_offRamp"); - address internal s_burnMintOnRamp = makeAddr("burn_mint_onRamp"); - - UpgradeableBurnMintTokenPool internal s_pool; - - function setUp() public virtual override(RouterSetup, BaseTest) { - RouterSetup.setUp(); - - // GHO deployment - GhoToken ghoToken = new GhoToken(AAVE_DAO); - s_burnMintERC677 = BurnMintERC677(address(ghoToken)); - - s_pool = UpgradeableBurnMintTokenPool( - _deployUpgradeableBurnMintTokenPoolOld( - address(s_burnMintERC677), - address(s_mockARM), - address(s_sourceRouter), - AAVE_DAO, - PROXY_ADMIN - ) - ); - - // Upgrade the old pool after deployment - _upgradeUpgradeableBurnMintTokenPool( - payable(address(s_pool)), - address(s_burnMintERC677), - address(s_mockARM), - PROXY_ADMIN - ); - - // Give mint and burn privileges to source UpgradeableTokenPool (GHO-specific related) - vm.startPrank(AAVE_DAO); - GhoToken(address(s_burnMintERC677)).grantRole( - GhoToken(address(s_burnMintERC677)).FACILITATOR_MANAGER_ROLE(), - AAVE_DAO - ); - GhoToken(address(s_burnMintERC677)).addFacilitator(address(s_pool), "UpgradeableTokenPool", type(uint128).max); - vm.stopPrank(); - - _applyChainUpdates(address(s_pool)); - } - - function _deployWithoutUpgrade() internal { - s_pool = UpgradeableBurnMintTokenPool( - _deployUpgradeableBurnMintTokenPoolOld( - address(s_burnMintERC677), - address(s_mockARM), - address(s_sourceRouter), - AAVE_DAO, - PROXY_ADMIN - ) - ); - - // Give mint and burn privileges to source UpgradeableTokenPool (GHO-specific related) - vm.stopPrank(); - vm.startPrank(AAVE_DAO); - GhoToken(address(s_burnMintERC677)).grantRole( - GhoToken(address(s_burnMintERC677)).FACILITATOR_MANAGER_ROLE(), - AAVE_DAO - ); - GhoToken(address(s_burnMintERC677)).addFacilitator(address(s_pool), "UpgradeableTokenPool", type(uint128).max); - vm.stopPrank(); - - _applyChainUpdates(address(s_pool)); - } - - function _applyChainUpdates(address pool) internal { - UpgradeableTokenPool.ChainUpdate[] memory chains = new UpgradeableTokenPool.ChainUpdate[](1); - chains[0] = UpgradeableTokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - allowed: true, - outboundRateLimiterConfig: getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: getInboundRateLimiterConfig() - }); - - vm.startPrank(AAVE_DAO); - UpgradeableBurnMintTokenPool(pool).applyChainUpdates(chains); - vm.stopPrank(); - vm.startPrank(OWNER); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_burnMintOnRamp}); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: DEST_CHAIN_SELECTOR, offRamp: s_burnMintOffRamp}); - s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - vm.stopPrank(); - } -} From a43c1747be185f8ffa0c21593772ee443653488b Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Fri, 19 Jul 2024 15:33:01 -0700 Subject: [PATCH 14/53] test: simple tests showing upgrade --- .../GHO/UpgradeableBurnMintTokenPool.sol | 2 +- .../ccip/test/pools/GHO/GhoBaseTest.t.sol | 33 ++++++++- .../pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol | 67 +++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index cbfb88567d..a46ff915e5 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -88,4 +88,4 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken function _burn(uint256 amount) internal virtual override { IBurnMintERC20(address(i_token)).burn(amount); } -} \ No newline at end of file +} 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 8f1b66d216..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, @@ -279,4 +310,4 @@ abstract contract GhoBaseTest is BaseTest { function _isEthereumChain(uint256 chainId) internal pure returns (bool) { return chainId == 0; } -} \ No newline at end of file +} 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..f7e5f48f15 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol @@ -0,0 +1,67 @@ +// 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)); + } +} From 5bf3723c18a48bc94a84a55bf9d80e95ef4bbec8 Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Mon, 22 Jul 2024 09:07:49 -0700 Subject: [PATCH 15/53] fix: add and correct comments --- .../src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol | 3 ++- .../v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index a46ff915e5..fbe8bad3ee 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -18,6 +18,7 @@ import {IRouter} from "../../interfaces/IRouter.sol"; /// @dev Contract adaptations: /// - Implementation of Initializable to allow upgrades /// - Move of allowlist and router definition to initialization stage +/// - Inclusion of rate limit admin who may configure rate limits in addition to owner contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { error Unauthorized(address caller); @@ -68,7 +69,7 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken return s_rateLimitAdmin; } - /// @notice Sets the rate limiter admin address. + /// @notice Sets the rate limiter config. /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal /// onlyAdmin check in the base implementation to also allow the rate limiter admin. /// @param remoteChainSelector The remote chain selector for which the rate limits apply. diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index 9a30b1e977..8395cdb28c 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -248,7 +248,7 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, emit LiquidityRemoved(msg.sender, amount); } - /// @notice Sets the rate limiter admin address. + /// @notice Sets the rate limiter config. /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal /// onlyAdmin check in the base implementation to also allow the rate limiter admin. /// @param remoteChainSelector The remote chain selector for which the rate limits apply. From e41fab2631bb6956173b6ca76139614b6b8bcc36 Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Mon, 22 Jul 2024 09:14:19 -0700 Subject: [PATCH 16/53] fix: comments to match parent contract --- .../src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol | 2 +- .../src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index fbe8bad3ee..33c42760b7 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -69,7 +69,7 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken return s_rateLimitAdmin; } - /// @notice Sets the rate limiter config. + /// @notice Sets the chain rate limiter config. /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal /// onlyAdmin check in the base implementation to also allow the rate limiter admin. /// @param remoteChainSelector The remote chain selector for which the rate limits apply. diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index 8395cdb28c..8001ae59fa 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -248,7 +248,7 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, emit LiquidityRemoved(msg.sender, amount); } - /// @notice Sets the rate limiter config. + /// @notice Sets the chain rate limiter config. /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal /// onlyAdmin check in the base implementation to also allow the rate limiter admin. /// @param remoteChainSelector The remote chain selector for which the rate limits apply. From d2eb1385882a704d3707d07ed504c2050ed3a54a Mon Sep 17 00:00:00 2001 From: CheyenneAtapour Date: Mon, 22 Jul 2024 11:12:08 -0700 Subject: [PATCH 17/53] chore: prettier --- .../pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) 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 index f7e5f48f15..763f6303bd 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol @@ -9,13 +9,15 @@ import {GhoTokenPoolRemoteSetup} from "./GhoTokenPoolRemoteSetup.t.sol"; contract GhoTokenPoolRemoteUpgrade is GhoTokenPoolRemoteSetup { // Unable to call setRateLimitAdmin() before upgrade function testSetRateLimitAdminRevertsBeforeUpgrade() public { - s_pool = UpgradeableBurnMintTokenPool(_deployUpgradeableBurnMintTokenPoolOld( + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( address(s_burnMintERC677), address(s_mockARM), address(s_sourceRouter), - AAVE_DAO, + AAVE_DAO, PROXY_ADMIN - )); + ) + ); vm.prank(AAVE_DAO); vm.expectRevert(); s_pool.setRateLimitAdmin(AAVE_DAO); @@ -24,16 +26,22 @@ contract GhoTokenPoolRemoteUpgrade is GhoTokenPoolRemoteSetup { // Able to call setRateLimitAdmin() after upgrade function testUpgradeAndSetRateLimitAdmin() public { // Assume existing remote pool as is deployed - s_pool = UpgradeableBurnMintTokenPool(_deployUpgradeableBurnMintTokenPoolOld( + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( address(s_burnMintERC677), address(s_mockARM), address(s_sourceRouter), - AAVE_DAO, + AAVE_DAO, PROXY_ADMIN - )); - + ) + ); + // Deploy new implementation - UpgradeableBurnMintTokenPool tokenPoolImpl = new UpgradeableBurnMintTokenPool(address(s_burnMintERC677), address(s_mockARM), false); + 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)); @@ -46,16 +54,22 @@ contract GhoTokenPoolRemoteUpgrade is GhoTokenPoolRemoteSetup { // Unable to call initialize() on proxy after upgrade function testInitializeRevertsAfterUpgrade() public { - s_pool = UpgradeableBurnMintTokenPool(_deployUpgradeableBurnMintTokenPoolOld( + s_pool = UpgradeableBurnMintTokenPool( + _deployUpgradeableBurnMintTokenPoolOld( address(s_burnMintERC677), address(s_mockARM), address(s_sourceRouter), - AAVE_DAO, + AAVE_DAO, PROXY_ADMIN - )); + ) + ); // Deploy new implementation - UpgradeableBurnMintTokenPool tokenPoolImpl = new UpgradeableBurnMintTokenPool(address(s_burnMintERC677), address(s_mockARM), false); + 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)); From 6d355246c18f490f5071f1ecaefb22bfae5bb092 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:47:21 +0530 Subject: [PATCH 18/53] fix: allow calls 1.2 on ramp during 1.5 migration by introducing legacyOnRamp --- .../ccip/pools/GHO/UpgradeableTokenPool.sol | 32 ++++++++++++++- .../test/pools/GHO/GhoTokenPoolRemote.t.sol | 39 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index ee359ac1f8..4a6e65acb7 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -55,6 +55,11 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain } + /// @dev The storage slot for the legacy onRamp address. + /// @dev This was added to continue support for 1.2 onRamp during 1.5 migration, and is stored + /// this way to avoid storage collision. + bytes32 internal constant LEGACY_ON_RAMP_STORAGE_SLOT = + 0x1502550b48234eae00e6e40fbe138bacb20fb810a4e848e89695d938ac3530a6; // bytes32(uint256(keccak256("ccip.pools.GHO.UpgradeableTokenPool.legacyOnRamp")) - 1) /// @dev The bridgeable token that is managed by this pool. IERC20 internal immutable i_token; /// @dev The address of the arm proxy @@ -250,7 +255,8 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); + if (!(msg.sender == s_router.getOnRamp(remoteChainSelector) && msg.sender != getLegacyOnRamp(remoteChainSelector))) + revert CallerIsNotARampOnRouter(msg.sender); _; } @@ -317,4 +323,28 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); _; } + + /// @notice Getter for legacy onRamp address. + /// @param remoteChainSelector The remote chain selector for which the legacy onRamp is being retrieved. + /// @return legacyOnRamp The legacy onRamp address for the given remoteChainSelector + function getLegacyOnRamp(uint64 remoteChainSelector) public view returns (address legacyOnRamp) { + assembly ("memory-safe") { + mstore(0, LEGACY_ON_RAMP_STORAGE_SLOT) + mstore(32, remoteChainSelector) + legacyOnRamp := shr(96, shl(96, sload(keccak256(0, 64)))) + } + } + + /// @notice Setter for legacy onRamp address, only callable by the DAO. + /// @param remoteChainSelector The remote chain selector for which the legacy onRamp is being set. + /// @param legacyOnRamp The address of the legacy onRamp. + function setLegacyOnRamp(uint64 remoteChainSelector, address legacyOnRamp) external onlyOwner { + if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); + // todo: add check for typeAndVersion or getStaticConfig().destChainSelector on legacyOnRamp? + assembly ("memory-safe") { + mstore(0, LEGACY_ON_RAMP_STORAGE_SLOT) + mstore(32, remoteChainSelector) + sstore(keccak256(0, 64), legacyOnRamp) + } + } } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index 773528c715..975c7ab5a9 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -376,3 +376,42 @@ contract GhoTokenPoolRemote_setRateLimitAdmin is GhoTokenPoolRemoteSetup { s_pool.setRateLimitAdmin(STRANGER); } } + +contract GhoTokenPoolRemote_legacyOnRamp is GhoTokenPoolRemoteSetup { + function testSetLegacyOnRampAdminReverts() public { + vm.startPrank(STRANGER); + vm.expectRevert("Only callable by owner"); + s_pool.setLegacyOnRamp(DEST_CHAIN_SELECTOR, makeAddr("legacyOnRamp")); + } + + function testSetLegacyOnRampInvalidChainReverts(uint64 nonExistentChainSelector) public { + vm.assume(nonExistentChainSelector != DEST_CHAIN_SELECTOR); + changePrank(AAVE_DAO); + vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ChainNotAllowed.selector, nonExistentChainSelector)); + s_pool.setLegacyOnRamp(nonExistentChainSelector, makeAddr("legacyOnRamp")); + } + + function testSetLegacyOnRampSuccess(address legacyOnRamp) public { + changePrank(AAVE_DAO); + s_pool.setLegacyOnRamp(DEST_CHAIN_SELECTOR, legacyOnRamp); + + assertEq(s_pool.getLegacyOnRamp(DEST_CHAIN_SELECTOR), legacyOnRamp); + } + + function testFuzzGetLegacyOnRamp(uint64 chainSelector, address legacyOnRamp) public { + vm.assume(chainSelector != DEST_CHAIN_SELECTOR); + UpgradeableTokenPool.ChainUpdate[] memory chains = new UpgradeableTokenPool.ChainUpdate[](1); + chains[0] = UpgradeableTokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + allowed: true, + outboundRateLimiterConfig: getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: getInboundRateLimiterConfig() + }); + + changePrank(AAVE_DAO); + s_pool.applyChainUpdates(chains); // more robust than modifying `s_remoteChainSelectors` set storage + s_pool.setLegacyOnRamp(chainSelector, legacyOnRamp); + + assertEq(s_pool.getLegacyOnRamp(chainSelector), legacyOnRamp); + } +} From 126311b3ffc8d91e3027e6f45d08cbb026e54bb5 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:48:20 +0530 Subject: [PATCH 19/53] chore: update certora & compiler used to match forge --- .github/workflows/certora.yml | 6 +++--- certora/confs/ccip.conf | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/certora.yml b/.github/workflows/certora.yml index 7a2c63b81a..6a8e714868 100644 --- a/.github/workflows/certora.yml +++ b/.github/workflows/certora.yml @@ -20,13 +20,13 @@ jobs: with: { java-version: "11", java-package: jre } - name: Install certora cli - run: pip install certora-cli==7.6.3 + run: pip install certora-cli==7.17.2 - name: Install solc run: | - wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux + wget https://github.com/ethereum/solidity/releases/download/v0.8.19/solc-static-linux chmod +x solc-static-linux - sudo mv solc-static-linux /usr/local/bin/solc8.10 + sudo mv solc-static-linux /usr/local/bin/solc8.19 - name: Verify rule ${{ matrix.rule }} run: | diff --git a/certora/confs/ccip.conf b/certora/confs/ccip.conf index 02245a1353..6df7e32e37 100644 --- a/certora/confs/ccip.conf +++ b/certora/confs/ccip.conf @@ -13,7 +13,7 @@ "process": "emv", "prover_args": ["-depth 10","-mediumTimeout 700"], "smt_timeout": "600", - "solc": "solc8.10", + "solc": "solc8.19", "verify": "UpgradeableLockReleaseTokenPool:certora/specs/ccip.spec", "rule_sanity": "basic", "msg": "CCIP" From bf968daa0bcc60164699156d7d18d8e518424b51 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:52:18 +0530 Subject: [PATCH 20/53] test: add fork base --- .../ForkBase.t.sol | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol new file mode 100644 index 0000000000..96a4ff6450 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {Test} from "forge-std/Test.sol"; +import {IERC20} from "../../../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {UpgradeableLockReleaseTokenPool} from "../../../../../pools/GHO/UpgradeableLockReleaseTokenPool.sol"; +import {UpgradeableBurnMintTokenPool} from "../../../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {IRouterClient} from "../../../../../interfaces/IRouterClient.sol"; +import {Client} from "../../../../../libraries/Client.sol"; + +contract ForkBase is Test { + error CallerIsNotARampOnRouter(address caller); + + struct L1 { + UpgradeableLockReleaseTokenPool tokenPool; + IRouterClient router; + IERC20 token; + address proxyPool; + uint64 chainSelector; + uint forkId; + } + struct L2 { + UpgradeableBurnMintTokenPool tokenPool; + IRouterClient router; + IERC20 token; + address proxyPool; + uint64 chainSelector; + uint forkId; + } + + L1 internal l1; + L2 internal l2; + + address internal alice = makeAddr("alice"); + + function setUp() public virtual { + l1.forkId = vm.createFork("https://sepolia.gateway.tenderly.co", 6884195); + l2.forkId = vm.createFork("https://arbitrum-sepolia.gateway.tenderly.co", 89058935); + + vm.selectFork(l1.forkId); + l1.tokenPool = UpgradeableLockReleaseTokenPool(0x7768248E1Ff75612c18324bad06bb393c1206980); + l1.proxyPool = 0x14A3298f667CCB3ad4B77878d80b353f6A10F183; + l1.router = IRouterClient(l1.tokenPool.getRouter()); + l2.chainSelector = l1.tokenPool.getSupportedChains()[0]; + l1.token = l1.tokenPool.getToken(); + vm.prank(alice); + l1.token.approve(address(l1.router), type(uint256).max); + deal(address(l1.token), alice, 1000e18); + deal(alice, 1000e18); + + vm.selectFork(l2.forkId); + l2.tokenPool = UpgradeableBurnMintTokenPool(0x3eC2b6F818B72442fc36561e9F930DD2b60957D2); + l2.proxyPool = 0x2BDbDCC0957E8d9f5Eb1Fe8E1Bc0d7F57AD1C897; + l2.router = IRouterClient(l2.tokenPool.getRouter()); + l1.chainSelector = l2.tokenPool.getSupportedChains()[0]; + l2.token = l2.tokenPool.getToken(); + vm.prank(alice); + l2.token.approve(address(l2.router), type(uint256).max); + deal(address(l2.token), alice, 1000e18); + deal(alice, 1000e18); + + vm.selectFork(l1.forkId); + assertEq(address(l1.router), 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59); + assertEq(l1.chainSelector, 16015286601757825753); + assertEq(address(l1.token), 0xc4bF5CbDaBE595361438F8c6a187bDc330539c60); + assertEq(l1.token.balanceOf(alice), 1000e18); + assertEq(l1.proxyPool, 0x14A3298f667CCB3ad4B77878d80b353f6A10F183); + + vm.selectFork(l2.forkId); + assertEq(address(l2.router), 0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165); + assertEq(l2.chainSelector, 3478487238524512106); + assertEq(address(l2.token), 0xb13Cfa6f8B2Eed2C37fB00fF0c1A59807C585810); + assertEq(l2.token.balanceOf(alice), 1000e18); + assertEq(l2.proxyPool, 0x2BDbDCC0957E8d9f5Eb1Fe8E1Bc0d7F57AD1C897); + + _label(); + } + + function _selectForkAndStartPrank(uint forkId) internal { + vm.selectFork(forkId); + vm.startPrank(alice); + } + + function _label() internal { + vm.label(address(l1.tokenPool), "l1.tokenPool"); + vm.label(address(l1.token), "l1.token"); + vm.label(address(l1.router), "l1.router"); + vm.label(address(l1.proxyPool), "l1.proxyPool"); + + vm.label(address(l2.tokenPool), "l2.tokenPool"); + vm.label(address(l2.token), "l2.token"); + vm.label(address(l2.router), "l2.router"); + vm.label(address(l2.proxyPool), "l2.proxyPool"); + } +} + +contract ForkBaseTest is ForkBase { + function setUp() public override { + super.setUp(); + } + + function test_currentSetupBroken() public { + uint256 amount = 10e18; + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(alice), + data: new bytes(0), + tokenAmounts: new Client.EVMTokenAmount[](1), + feeToken: address(0), // will be paying in native tokens for tests + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) + }); + message.tokenAmounts[0].token = address(l1.token); + message.tokenAmounts[0].amount = amount; + + vm.selectFork(l1.forkId); + uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + + vm.prank(alice); + vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, l1.proxyPool)); + l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); + + vm.selectFork(l2.forkId); + message.tokenAmounts[0].token = address(l2.token); + feeTokenAmount = l2.router.getFee(l1.chainSelector, message); + + vm.prank(alice); + vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, l2.proxyPool)); + l2.router.ccipSend{value: feeTokenAmount}(l1.chainSelector, message); + } +} From 65d0d11f26ce34190382e4cac29fb8209dcf843e Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:52:42 +0530 Subject: [PATCH 21/53] wip: fork upgrade test --- .../TokenPoolsUpgrade.t.sol | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol new file mode 100644 index 0000000000..6c8022fd95 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {ForkBase} from "./ForkBase.t.sol"; +import {UpgradeableLockReleaseTokenPool} from "../../../../../pools/GHO/UpgradeableLockReleaseTokenPool.sol"; +import {UpgradeableBurnMintTokenPool} from "../../../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "solidity-utils/contracts/transparent-proxy/ProxyAdmin.sol"; +import {Client} from "../../../../../libraries/Client.sol"; + +contract ForkTokenPoolsUpgrade is ForkBase { + function setUp() public override { + super.setUp(); + + _upgradeExistingLockReleaseTokenPool(); + _upgradeExistingBurnMintTokenPool(); + } + + function test_upgrade() public { + vm.selectFork(l1.forkId); + + uint256 amount = 10e18; + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(alice), + data: new bytes(0), + tokenAmounts: new Client.EVMTokenAmount[](1), + feeToken: address(0), // will be paying in native tokens for tests + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) + }); + message.tokenAmounts[0].token = address(l1.token); + message.tokenAmounts[0].amount = amount; + + uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + + vm.prank(alice); + vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, l1.proxyPool)); + l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); + } + + function _upgradeExistingLockReleaseTokenPool() internal { + vm.selectFork(l1.forkId); + UpgradeableLockReleaseTokenPool poolImpl = new UpgradeableLockReleaseTokenPool( + address(l1.token), + l1.tokenPool.getArmProxy(), + l1.tokenPool.getAllowListEnabled(), + l1.tokenPool.canAcceptLiquidity() + ); + _upgradeProxy(TransparentUpgradeableProxy(payable(address(l1.tokenPool))), address(poolImpl)); + } + + function _upgradeExistingBurnMintTokenPool() internal { + vm.selectFork(l2.forkId); + UpgradeableBurnMintTokenPool poolImpl = new UpgradeableBurnMintTokenPool( + address(l2.token), + l2.tokenPool.getArmProxy(), + l2.tokenPool.getAllowListEnabled() + ); + _upgradeProxy(TransparentUpgradeableProxy(payable(address(l2.tokenPool))), address(poolImpl)); + } + + function _upgradeProxy(TransparentUpgradeableProxy proxy, address impl) private { + address proxyAdminAddress = address( + uint160(uint256(vm.load(address(proxy), bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)))) + ); + assertNotEq(proxyAdminAddress, address(0), "version mismatch: proxyAdmin"); + if (proxyAdminAddress.code.length != 0) { + ProxyAdmin proxyAdmin = ProxyAdmin(proxyAdminAddress); + assertEq(proxyAdmin.getProxyAdmin(proxy), address(proxyAdmin)); + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade(proxy, address(impl)); + } else { + // sepolia has proxy admin as an eoa + vm.prank(proxyAdminAddress); + proxy.upgradeTo(address(impl)); + } + } +} From c1ae0c998e96019fd4e35678c9d52ee72e1bffe2 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:13:55 +0530 Subject: [PATCH 22/53] fix: onlyOnRamp condition --- contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 4a6e65acb7..8534ad45b8 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -255,7 +255,7 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector) && msg.sender != getLegacyOnRamp(remoteChainSelector))) + if (msg.sender != s_router.getOnRamp(remoteChainSelector) && msg.sender != getLegacyOnRamp(remoteChainSelector)) revert CallerIsNotARampOnRouter(msg.sender); _; } From 88184aacfbbd16e9462e2884e0d66b445ac17e2d Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:14:34 +0530 Subject: [PATCH 23/53] test: add testnet legacy pools --- ...pgradeableBurnMintTokenPool_ArbSepolia.sol | 68 +++++ ...pgradeableLockReleaseTokenPool_Sepolia.sol | 278 ++++++++++++++++++ .../VersionedInitializable.sol | 77 +++++ 3 files changed, 423 insertions(+) create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol create mode 100644 contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/VersionedInitializable.sol diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol new file mode 100644 index 0000000000..3fbf062b63 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {ITypeAndVersion} from "../../../../../../../shared/interfaces/ITypeAndVersion.sol"; +import {IBurnMintERC20} from "../../../../../../../shared/token/ERC20/IBurnMintERC20.sol"; +import {UpgradeableTokenPool} from "../../../../../../pools/GHO/UpgradeableTokenPool.sol"; +import {UpgradeableBurnMintTokenPoolAbstract} from "../../../../../../pools/GHO/UpgradeableBurnMintTokenPoolAbstract.sol"; +import {IRouter} from "../../../../../../interfaces/IRouter.sol"; +import {VersionedInitializable} from "./VersionedInitializable.sol"; + +/// @title UpgradeableBurnMintTokenPool_ArbSepolia +/// @author Aave Labs +/// @notice Upgradeable version of Chainlink's CCIP BurnMintTokenPool +/// @dev Contract adaptations: +/// - Implementation of VersionedInitializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage +contract UpgradeableBurnMintTokenPool_ArbSepolia is + VersionedInitializable, + 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); + } + + /// @notice Returns the revision number + /// @return The revision number + function REVISION() public pure virtual returns (uint256) { + return 1; + } + + /// @inheritdoc VersionedInitializable + function getRevision() internal pure virtual override returns (uint256) { + return REVISION(); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol new file mode 100644 index 0000000000..748ebd7d84 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {ITypeAndVersion} from "../../../../../../../shared/interfaces/ITypeAndVersion.sol"; + +import {ILiquidityContainer} from "../../../../../../../rebalancer/interfaces/ILiquidityContainer.sol"; + +import {UpgradeableTokenPool} from "../../../../../../pools/GHO/UpgradeableTokenPool.sol"; +import {RateLimiter} from "../../../../../../libraries/RateLimiter.sol"; + +import {IERC20} from "../../../../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "../../../../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; + +import {IRouter} from "../../../../../../interfaces/IRouter.sol"; +import {VersionedInitializable} from "./VersionedInitializable.sol"; + +/// @title UpgradeableLockReleaseTokenPool_Sepolia +/// @author Aave Labs +/// @notice Upgradeable version of Chainlink's CCIP LockReleaseTokenPool +/// @dev Contract adaptations: +/// - Implementation of VersionedInitializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage +/// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked) +contract UpgradeableLockReleaseTokenPool_Sepolia is + VersionedInitializable, + UpgradeableTokenPool, + ILiquidityContainer, + ITypeAndVersion +{ + using SafeERC20 for IERC20; + + error InsufficientLiquidity(); + error LiquidityNotAccepted(); + error Unauthorized(address caller); + + error BridgeLimitExceeded(uint256 bridgeLimit); + error NotEnoughBridgedAmount(); + event BridgeLimitUpdated(uint256 oldBridgeLimit, uint256 newBridgeLimit); + + string public constant override typeAndVersion = "LockReleaseTokenPool 1.4.0"; + + /// @dev The unique lock release pool flag to signal through EIP 165. + bytes4 private constant LOCK_RELEASE_INTERFACE_ID = bytes4(keccak256("LockReleaseTokenPool")); + + /// @dev Whether or not the pool accepts liquidity. + /// External liquidity is not required when there is one canonical token deployed to a chain, + /// and CCIP is facilitating mint/burn on all the other chains, in which case the invariant + /// balanceOf(pool) on home chain == sum(totalSupply(mint/burn "wrapped" token) on all remote chains) should always hold + bool internal immutable i_acceptLiquidity; + /// @notice The address of the rebalancer. + address internal s_rebalancer; + /// @notice The address of the rate limiter admin. + /// @dev Can be address(0) if none is configured. + address internal s_rateLimitAdmin; + + /// @notice Maximum amount of tokens that can be bridged to other chains + uint256 private s_bridgeLimit; + /// @notice Amount of tokens bridged (transferred out) + /// @dev Must always be equal to or below the bridge limit + uint256 private s_currentBridged; + /// @notice The address of the bridge limit admin. + /// @dev Can be address(0) if none is configured. + address internal s_bridgeLimitAdmin; + + /// @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 + /// @param acceptLiquidity True if the pool accepts liquidity, false otherwise + constructor( + address token, + address armProxy, + bool allowlistEnabled, + bool acceptLiquidity + ) UpgradeableTokenPool(IERC20(token), armProxy, allowlistEnabled) { + i_acceptLiquidity = acceptLiquidity; + } + + /// @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 + /// @param bridgeLimit The maximum amount of tokens that can be bridged to other chains + function initialize( + address owner, + address[] memory allowlist, + address router, + uint256 bridgeLimit + ) 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); + } + s_bridgeLimit = bridgeLimit; + } + + /// @notice Locks the token in the pool + /// @param amount Amount to lock + /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised + /// we're able to stop token movement via ARM. + function lockOrBurn( + address originalSender, + bytes calldata, + uint256 amount, + uint64 remoteChainSelector, + bytes calldata + ) + external + virtual + override + onlyOnRamp(remoteChainSelector) + checkAllowList(originalSender) + whenHealthy + returns (bytes memory) + { + // Increase bridged amount because tokens are leaving the source chain + if ((s_currentBridged += amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit); + + _consumeOutboundRateLimit(remoteChainSelector, amount); + emit Locked(msg.sender, amount); + return ""; + } + + /// @notice Release tokens from the pool to the recipient + /// @param receiver Recipient address + /// @param amount Amount to release + /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised + /// we're able to stop token movement via ARM. + function releaseOrMint( + bytes memory, + address receiver, + uint256 amount, + uint64 remoteChainSelector, + bytes memory + ) external virtual override onlyOffRamp(remoteChainSelector) whenHealthy { + // This should never occur. Amount should never exceed the current bridged amount + if (amount > s_currentBridged) revert NotEnoughBridgedAmount(); + // Reduce bridged amount because tokens are back to source chain + s_currentBridged -= amount; + + _consumeInboundRateLimit(remoteChainSelector, amount); + getToken().safeTransfer(receiver, amount); + emit Released(msg.sender, receiver, amount); + } + + /// @notice returns the lock release interface flag used for EIP165 identification. + function getLockReleaseInterfaceId() public pure returns (bytes4) { + return LOCK_RELEASE_INTERFACE_ID; + } + + // @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { + return + interfaceId == LOCK_RELEASE_INTERFACE_ID || + interfaceId == type(ILiquidityContainer).interfaceId || + super.supportsInterface(interfaceId); + } + + /// @notice Gets Rebalancer, can be address(0) if none is configured. + /// @return The current liquidity manager. + function getRebalancer() external view returns (address) { + return s_rebalancer; + } + + /// @notice Sets the Rebalancer address. + /// @dev Only callable by the owner. + function setRebalancer(address rebalancer) external onlyOwner { + s_rebalancer = rebalancer; + } + + /// @notice Sets the rate limiter admin address. + /// @dev Only callable by the owner. + /// @param rateLimitAdmin The new rate limiter admin address. + function setRateLimitAdmin(address rateLimitAdmin) external onlyOwner { + s_rateLimitAdmin = rateLimitAdmin; + } + + /// @notice Sets the bridge limit, the maximum amount of tokens that can be bridged out + /// @dev Only callable by the owner or the bridge limit admin. + /// @dev Bridge limit changes should be carefully managed, specially when reducing below the current bridged amount + /// @param newBridgeLimit The new bridge limit + function setBridgeLimit(uint256 newBridgeLimit) external { + if (msg.sender != s_bridgeLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + uint256 oldBridgeLimit = s_bridgeLimit; + s_bridgeLimit = newBridgeLimit; + emit BridgeLimitUpdated(oldBridgeLimit, newBridgeLimit); + } + + /// @notice Sets the bridge limit admin address. + /// @dev Only callable by the owner. + /// @param bridgeLimitAdmin The new bridge limit admin address. + function setBridgeLimitAdmin(address bridgeLimitAdmin) external onlyOwner { + s_bridgeLimitAdmin = bridgeLimitAdmin; + } + + /// @notice Gets the bridge limit + /// @return The maximum amount of tokens that can be transferred out to other chains + function getBridgeLimit() external view virtual returns (uint256) { + return s_bridgeLimit; + } + + /// @notice Gets the current bridged amount to other chains + /// @return The amount of tokens transferred out to other chains + function getCurrentBridgedAmount() external view virtual returns (uint256) { + return s_currentBridged; + } + + /// @notice Gets the rate limiter admin address. + function getRateLimitAdmin() external view returns (address) { + return s_rateLimitAdmin; + } + + /// @notice Gets the bridge limiter admin address. + function getBridgeLimitAdmin() external view returns (address) { + return s_bridgeLimitAdmin; + } + + /// @notice Checks if the pool can accept liquidity. + /// @return true if the pool can accept liquidity, false otherwise. + function canAcceptLiquidity() external view returns (bool) { + return i_acceptLiquidity; + } + + /// @notice Adds liquidity to the pool. The tokens should be approved first. + /// @param amount The amount of liquidity to provide. + function provideLiquidity(uint256 amount) external { + if (!i_acceptLiquidity) revert LiquidityNotAccepted(); + if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender); + + i_token.safeTransferFrom(msg.sender, address(this), amount); + emit LiquidityAdded(msg.sender, amount); + } + + /// @notice Removed liquidity to the pool. The tokens will be sent to msg.sender. + /// @param amount The amount of liquidity to remove. + function withdrawLiquidity(uint256 amount) external { + if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender); + + if (i_token.balanceOf(address(this)) < amount) revert InsufficientLiquidity(); + i_token.safeTransfer(msg.sender, amount); + emit LiquidityRemoved(msg.sender, amount); + } + + /// @notice Sets the rate limiter admin address. + /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal + /// onlyAdmin check in the base implementation to also allow the rate limiter admin. + /// @param remoteChainSelector The remote chain selector for which the rate limits apply. + /// @param outboundConfig The new outbound rate limiter config. + /// @param inboundConfig The new inbound rate limiter config. + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + RateLimiter.Config memory outboundConfig, + RateLimiter.Config memory inboundConfig + ) external override { + if (msg.sender != s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + + _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); + } + + /// @notice Returns the revision number + /// @return The revision number + function REVISION() public pure virtual returns (uint256) { + return 1; + } + + /// @inheritdoc VersionedInitializable + function getRevision() internal pure virtual override returns (uint256) { + return REVISION(); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/VersionedInitializable.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/VersionedInitializable.sol new file mode 100644 index 0000000000..b9fb054fa0 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/LegacyTestnetTokenPools/VersionedInitializable.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +/** + * @title VersionedInitializable + * @author Aave, inspired by the OpenZeppelin Initializable contract + * @notice Helper contract to implement initializer functions. To use it, replace + * the constructor with a function that has the `initializer` modifier. + * @dev WARNING: Unlike constructors, initializer functions must be manually + * invoked. This applies both to deploying an Initializable contract, as well + * as extending an Initializable contract via inheritance. + * WARNING: When used with inheritance, manual care must be taken to not invoke + * a parent initializer twice, or ensure that all initializers are idempotent, + * because this is not dealt with automatically as with constructors. + */ +abstract contract VersionedInitializable { + /** + * @dev Indicates that the contract has been initialized. + */ + uint256 private lastInitializedRevision = 0; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private initializing; + + /** + * @dev Modifier to use in the initializer function of a contract. + */ + modifier initializer() { + uint256 revision = getRevision(); + require( + initializing || isConstructor() || revision > lastInitializedRevision, + "Contract instance has already been initialized" + ); + + bool isTopLevelCall = !initializing; + if (isTopLevelCall) { + initializing = true; + lastInitializedRevision = revision; + } + + _; + + if (isTopLevelCall) { + initializing = false; + } + } + + /** + * @notice Returns the revision number of the contract + * @dev Needs to be defined in the inherited class as a constant. + * @return The revision number + */ + function getRevision() internal pure virtual returns (uint256); + + /** + * @notice Returns true if and only if the function is running in the constructor + * @return True if the function is running in the constructor + */ + function isConstructor() private view returns (bool) { + // extcodesize checks the size of the code stored in an address, and + // address returns the current address. Since the code is still not + // deployed when running a constructor, any checks on its code size will + // yield zero, making it an effective way to detect if a contract is + // under construction or not. + uint256 cs; + //solium-disable-next-line + assembly { + cs := extcodesize(address()) + } + return cs == 0; + } + + // Reserved storage space to allow for layout changes in the future. + uint256[50] private ______gap; +} From 3a74f8692edda99d03bd3fbd1f39af0143d4f7ab Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:15:30 +0530 Subject: [PATCH 24/53] test: successful send with upgrade --- .../ForkBase.t.sol | 151 +++++++++++++++--- .../TokenPoolsUpgrade.t.sol | 39 +++-- 2 files changed, 149 insertions(+), 41 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 96a4ff6450..13f27b46e6 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -3,29 +3,50 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; import {IERC20} from "../../../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {UpgradeableLockReleaseTokenPool} from "../../../../../pools/GHO/UpgradeableLockReleaseTokenPool.sol"; -import {UpgradeableBurnMintTokenPool} from "../../../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol"; +import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; import {IRouterClient} from "../../../../../interfaces/IRouterClient.sol"; +import {IRouter as IRouterBase} from "../../../../../interfaces/IRouter.sol"; import {Client} from "../../../../../libraries/Client.sol"; +import {Internal} from "../../../../../libraries/Internal.sol"; +import {ITypeAndVersion} from "../../../../../../shared/interfaces/ITypeAndVersion.sol"; + +interface IRouter is IRouterClient, IRouterBase { + function getWrappedNative() external view returns (address); +} + +struct SourceTokenData { + bytes sourcePoolAddress; + bytes destTokenAddress; + bytes extraData; + uint32 destGasAmount; +} contract ForkBase is Test { error CallerIsNotARampOnRouter(address caller); + event CCIPSendRequested(Internal.EVM2EVMMessage message); struct L1 { - UpgradeableLockReleaseTokenPool tokenPool; - IRouterClient router; + UpgradeableLockReleaseTokenPool_Sepolia tokenPool; + IRouter router; IERC20 token; + address EVM2EVMOnRamp1_2; + address EVM2EVMOnRamp1_5; address proxyPool; uint64 chainSelector; - uint forkId; + bytes32 metadataHash; + uint256 forkId; } struct L2 { - UpgradeableBurnMintTokenPool tokenPool; - IRouterClient router; + UpgradeableBurnMintTokenPool_ArbSepolia tokenPool; + IRouter router; IERC20 token; + address EVM2EVMOnRamp1_2; + address EVM2EVMOnRamp1_5; address proxyPool; uint64 chainSelector; - uint forkId; + bytes32 metadataHash; + uint256 forkId; } L1 internal l1; @@ -38,44 +59,126 @@ contract ForkBase is Test { l2.forkId = vm.createFork("https://arbitrum-sepolia.gateway.tenderly.co", 89058935); vm.selectFork(l1.forkId); - l1.tokenPool = UpgradeableLockReleaseTokenPool(0x7768248E1Ff75612c18324bad06bb393c1206980); + l1.tokenPool = UpgradeableLockReleaseTokenPool_Sepolia(0x7768248E1Ff75612c18324bad06bb393c1206980); l1.proxyPool = 0x14A3298f667CCB3ad4B77878d80b353f6A10F183; - l1.router = IRouterClient(l1.tokenPool.getRouter()); + l1.router = IRouter(l1.tokenPool.getRouter()); l2.chainSelector = l1.tokenPool.getSupportedChains()[0]; l1.token = l1.tokenPool.getToken(); + l1.EVM2EVMOnRamp1_2 = 0x1f41c443Cf68750d5c195E2EA7051521d981fC77; // legacy on ramp + l1.EVM2EVMOnRamp1_5 = l1.router.getOnRamp(l2.chainSelector); vm.prank(alice); l1.token.approve(address(l1.router), type(uint256).max); deal(address(l1.token), alice, 1000e18); deal(alice, 1000e18); vm.selectFork(l2.forkId); - l2.tokenPool = UpgradeableBurnMintTokenPool(0x3eC2b6F818B72442fc36561e9F930DD2b60957D2); + l2.tokenPool = UpgradeableBurnMintTokenPool_ArbSepolia(0x3eC2b6F818B72442fc36561e9F930DD2b60957D2); l2.proxyPool = 0x2BDbDCC0957E8d9f5Eb1Fe8E1Bc0d7F57AD1C897; - l2.router = IRouterClient(l2.tokenPool.getRouter()); + l2.router = IRouter(l2.tokenPool.getRouter()); l1.chainSelector = l2.tokenPool.getSupportedChains()[0]; l2.token = l2.tokenPool.getToken(); + l2.EVM2EVMOnRamp1_2 = 0xc1eBd046A4086142479bE3Fc16A4791E2022909a; // legacy on ramp + l2.EVM2EVMOnRamp1_5 = l2.router.getOnRamp(l1.chainSelector); vm.prank(alice); l2.token.approve(address(l2.router), type(uint256).max); deal(address(l2.token), alice, 1000e18); deal(alice, 1000e18); + l1.metadataHash = _generateMetadataHash(l1.chainSelector); + l2.metadataHash = _generateMetadataHash(l2.chainSelector); + vm.selectFork(l1.forkId); - assertEq(address(l1.router), 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59); assertEq(l1.chainSelector, 16015286601757825753); assertEq(address(l1.token), 0xc4bF5CbDaBE595361438F8c6a187bDc330539c60); assertEq(l1.token.balanceOf(alice), 1000e18); - assertEq(l1.proxyPool, 0x14A3298f667CCB3ad4B77878d80b353f6A10F183); + assertEq(ITypeAndVersion(address(l1.router)).typeAndVersion(), "Router 1.2.0"); + assertEq(ITypeAndVersion(l1.proxyPool).typeAndVersion(), "LockReleaseTokenPoolAndProxy 1.5.0"); + assertEq(ITypeAndVersion(l1.EVM2EVMOnRamp1_2).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(ITypeAndVersion(l1.EVM2EVMOnRamp1_5).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); vm.selectFork(l2.forkId); - assertEq(address(l2.router), 0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165); assertEq(l2.chainSelector, 3478487238524512106); assertEq(address(l2.token), 0xb13Cfa6f8B2Eed2C37fB00fF0c1A59807C585810); assertEq(l2.token.balanceOf(alice), 1000e18); - assertEq(l2.proxyPool, 0x2BDbDCC0957E8d9f5Eb1Fe8E1Bc0d7F57AD1C897); + assertEq(ITypeAndVersion(address(l2.router)).typeAndVersion(), "Router 1.2.0"); + assertEq(ITypeAndVersion(l2.proxyPool).typeAndVersion(), "BurnMintTokenPoolAndProxy 1.5.0"); + assertEq(ITypeAndVersion(l2.EVM2EVMOnRamp1_2).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(ITypeAndVersion(l2.EVM2EVMOnRamp1_5).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); _label(); } + function _generateMessage( + address receiver, + uint256 tokenAmountsLength + ) internal view returns (Client.EVM2AnyMessage memory) { + return + Client.EVM2AnyMessage({ + receiver: abi.encode(receiver), + data: "", + tokenAmounts: new Client.EVMTokenAmount[](tokenAmountsLength), + feeToken: address(0), + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) + }); + } + + function _messageToEvent( + Client.EVM2AnyMessage memory message, + uint64 seqNum, + uint64 nonce, + uint256 feeTokenAmount, + address originalSender, + bytes32 metadataHash, + uint32 destGasAmount + ) public view returns (Internal.EVM2EVMMessage memory) { + address feeToken = metadataHash == l1.metadataHash ? l1.router.getWrappedNative() : l2.router.getWrappedNative(); + address destTokenAddress = metadataHash == l1.metadataHash ? address(l2.token) : address(l1.token); + address sourcePool = metadataHash == l1.metadataHash ? l1.proxyPool : l2.proxyPool; + + // Slicing is only available for calldata. So we have to build a new bytes array. + bytes memory args = new bytes(message.extraArgs.length - 4); + for (uint256 i = 4; i < message.extraArgs.length; ++i) { + args[i - 4] = message.extraArgs[i]; + } + Client.EVMExtraArgsV1 memory extraArgs = abi.decode(args, (Client.EVMExtraArgsV1)); + Internal.EVM2EVMMessage memory messageEvent = Internal.EVM2EVMMessage({ + sequenceNumber: seqNum, + feeTokenAmount: feeTokenAmount, + sender: originalSender, + nonce: nonce, + gasLimit: extraArgs.gasLimit, + strict: false, + sourceChainSelector: l1.chainSelector, + receiver: abi.decode(message.receiver, (address)), + data: message.data, + tokenAmounts: message.tokenAmounts, + sourceTokenData: new bytes[](message.tokenAmounts.length), + feeToken: feeToken, + messageId: "" + }); + + for (uint256 i; i < message.tokenAmounts.length; ++i) { + // change introduced in 1.5 upgrade + messageEvent.sourceTokenData[i] = abi.encode( + SourceTokenData({ + sourcePoolAddress: abi.encode(sourcePool), + destTokenAddress: abi.encode(destTokenAddress), + extraData: "", + destGasAmount: destGasAmount + }) + ); + } + + messageEvent.messageId = Internal._hash(messageEvent, metadataHash); + return messageEvent; + } + + function _generateMetadataHash(uint64 sourceChainSelector) internal view returns (bytes32) { + uint64 destChainSelector = sourceChainSelector == l1.chainSelector ? l2.chainSelector : l1.chainSelector; + address onRamp = sourceChainSelector == l1.chainSelector ? l1.EVM2EVMOnRamp1_5 : l2.EVM2EVMOnRamp1_5; + return keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, destChainSelector, onRamp)); + } + function _selectForkAndStartPrank(uint forkId) internal { vm.selectFork(forkId); vm.startPrank(alice); @@ -86,28 +189,26 @@ contract ForkBase is Test { vm.label(address(l1.token), "l1.token"); vm.label(address(l1.router), "l1.router"); vm.label(address(l1.proxyPool), "l1.proxyPool"); + vm.label(address(l1.EVM2EVMOnRamp1_2), "l1.EVM2EVMOnRamp1_2"); + vm.label(address(l1.EVM2EVMOnRamp1_5), "l1.EVM2EVMOnRamp1_5"); vm.label(address(l2.tokenPool), "l2.tokenPool"); vm.label(address(l2.token), "l2.token"); vm.label(address(l2.router), "l2.router"); vm.label(address(l2.proxyPool), "l2.proxyPool"); + vm.label(address(l2.EVM2EVMOnRamp1_2), "l2.EVM2EVMOnRamp1_2"); + vm.label(address(l2.EVM2EVMOnRamp1_5), "l2.EVM2EVMOnRamp1_5"); } } -contract ForkBaseTest is ForkBase { +contract ForkPoolAfterMigration is ForkBase { function setUp() public override { super.setUp(); } - function test_currentSetupBroken() public { + function test_RevertSendLegacyPool() public { uint256 amount = 10e18; - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(alice), - data: new bytes(0), - tokenAmounts: new Client.EVMTokenAmount[](1), - feeToken: address(0), // will be paying in native tokens for tests - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) - }); + Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); message.tokenAmounts[0].token = address(l1.token); message.tokenAmounts[0].amount = amount; diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index 6c8022fd95..fb4cbf60d4 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -2,44 +2,52 @@ pragma solidity 0.8.19; import {ForkBase} from "./ForkBase.t.sol"; -import {UpgradeableLockReleaseTokenPool} from "../../../../../pools/GHO/UpgradeableLockReleaseTokenPool.sol"; -import {UpgradeableBurnMintTokenPool} from "../../../../../pools/GHO/UpgradeableBurnMintTokenPool.sol"; +import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol"; +import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; import {ProxyAdmin} from "solidity-utils/contracts/transparent-proxy/ProxyAdmin.sol"; import {Client} from "../../../../../libraries/Client.sol"; +import {Internal} from "../../../../../libraries/Internal.sol"; -contract ForkTokenPoolsUpgrade is ForkBase { +contract ForkPoolUpgradeAfterMigration is ForkBase { function setUp() public override { super.setUp(); + // #1: deploy new implementation & upgrade token pools + vm.selectFork(l1.forkId); _upgradeExistingLockReleaseTokenPool(); + + vm.selectFork(l2.forkId); _upgradeExistingBurnMintTokenPool(); + + // #2: update legacyOnRamp + vm.selectFork(l1.forkId); + vm.prank(l1.tokenPool.owner()); + l1.tokenPool.setLegacyOnRamp(l2.chainSelector, l1.proxyPool); + + vm.selectFork(l2.forkId); + vm.prank(l2.tokenPool.owner()); + l2.tokenPool.setLegacyOnRamp(l1.chainSelector, l2.proxyPool); } - function test_upgrade() public { + function test_sendAndReceiveFromL1() public { vm.selectFork(l1.forkId); uint256 amount = 10e18; - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(alice), - data: new bytes(0), - tokenAmounts: new Client.EVMTokenAmount[](1), - feeToken: address(0), // will be paying in native tokens for tests - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) - }); + Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); message.tokenAmounts[0].token = address(l1.token); message.tokenAmounts[0].amount = amount; uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + vm.expectEmit(); + emit CCIPSendRequested(_messageToEvent(message, 220, 1, feeTokenAmount, alice, l1.metadataHash, uint32(90000))); vm.prank(alice); - vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, l1.proxyPool)); l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); } function _upgradeExistingLockReleaseTokenPool() internal { - vm.selectFork(l1.forkId); - UpgradeableLockReleaseTokenPool poolImpl = new UpgradeableLockReleaseTokenPool( + UpgradeableLockReleaseTokenPool_Sepolia poolImpl = new UpgradeableLockReleaseTokenPool_Sepolia( address(l1.token), l1.tokenPool.getArmProxy(), l1.tokenPool.getAllowListEnabled(), @@ -49,8 +57,7 @@ contract ForkTokenPoolsUpgrade is ForkBase { } function _upgradeExistingBurnMintTokenPool() internal { - vm.selectFork(l2.forkId); - UpgradeableBurnMintTokenPool poolImpl = new UpgradeableBurnMintTokenPool( + UpgradeableBurnMintTokenPool_ArbSepolia poolImpl = new UpgradeableBurnMintTokenPool_ArbSepolia( address(l2.token), l2.tokenPool.getArmProxy(), l2.tokenPool.getAllowListEnabled() From 679c45fb09418bd59fd37e754853bc094b1dc913 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:57:27 +0530 Subject: [PATCH 25/53] test: add releaseOrMint tests --- .../ForkBase.t.sol | 20 ++++++++- .../TokenPoolsUpgrade.t.sol | 45 +++++++++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 13f27b46e6..7df1b46488 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -13,6 +13,7 @@ import {ITypeAndVersion} from "../../../../../../shared/interfaces/ITypeAndVersi interface IRouter is IRouterClient, IRouterBase { function getWrappedNative() external view returns (address); + function isOffRamp(uint64, address) external view returns (bool); } struct SourceTokenData { @@ -32,6 +33,8 @@ contract ForkBase is Test { IERC20 token; address EVM2EVMOnRamp1_2; address EVM2EVMOnRamp1_5; + address EVM2EVMOffRamp1_2; + address EVM2EVMOffRamp1_5; address proxyPool; uint64 chainSelector; bytes32 metadataHash; @@ -43,6 +46,8 @@ contract ForkBase is Test { IERC20 token; address EVM2EVMOnRamp1_2; address EVM2EVMOnRamp1_5; + address EVM2EVMOffRamp1_2; + address EVM2EVMOffRamp1_5; address proxyPool; uint64 chainSelector; bytes32 metadataHash; @@ -66,6 +71,8 @@ contract ForkBase is Test { l1.token = l1.tokenPool.getToken(); l1.EVM2EVMOnRamp1_2 = 0x1f41c443Cf68750d5c195E2EA7051521d981fC77; // legacy on ramp l1.EVM2EVMOnRamp1_5 = l1.router.getOnRamp(l2.chainSelector); + l1.EVM2EVMOffRamp1_2 = 0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1; + l1.EVM2EVMOffRamp1_5 = 0xD2f5edfD4561d6E7599F6c6888Bd353cAFd0c55E; vm.prank(alice); l1.token.approve(address(l1.router), type(uint256).max); deal(address(l1.token), alice, 1000e18); @@ -79,6 +86,8 @@ contract ForkBase is Test { l2.token = l2.tokenPool.getToken(); l2.EVM2EVMOnRamp1_2 = 0xc1eBd046A4086142479bE3Fc16A4791E2022909a; // legacy on ramp l2.EVM2EVMOnRamp1_5 = l2.router.getOnRamp(l1.chainSelector); + l2.EVM2EVMOffRamp1_2 = 0x1c71f141b4630EBE52d6aF4894812960abE207eB; + l2.EVM2EVMOffRamp1_5 = 0xBed6e9131916d724418C8a6FE810F727302a5c00; vm.prank(alice); l2.token.approve(address(l2.router), type(uint256).max); deal(address(l2.token), alice, 1000e18); @@ -95,6 +104,10 @@ contract ForkBase is Test { assertEq(ITypeAndVersion(l1.proxyPool).typeAndVersion(), "LockReleaseTokenPoolAndProxy 1.5.0"); assertEq(ITypeAndVersion(l1.EVM2EVMOnRamp1_2).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); assertEq(ITypeAndVersion(l1.EVM2EVMOnRamp1_5).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); + assertEq(ITypeAndVersion(l1.EVM2EVMOffRamp1_2).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); + assertEq(ITypeAndVersion(l1.EVM2EVMOffRamp1_5).typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); + assertTrue(l1.router.isOffRamp(l2.chainSelector, l1.EVM2EVMOffRamp1_2)); + assertTrue(l1.router.isOffRamp(l2.chainSelector, l1.EVM2EVMOffRamp1_5)); vm.selectFork(l2.forkId); assertEq(l2.chainSelector, 3478487238524512106); @@ -104,6 +117,10 @@ contract ForkBase is Test { assertEq(ITypeAndVersion(l2.proxyPool).typeAndVersion(), "BurnMintTokenPoolAndProxy 1.5.0"); assertEq(ITypeAndVersion(l2.EVM2EVMOnRamp1_2).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); assertEq(ITypeAndVersion(l2.EVM2EVMOnRamp1_5).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); + assertEq(ITypeAndVersion(l2.EVM2EVMOffRamp1_2).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); + assertEq(ITypeAndVersion(l2.EVM2EVMOffRamp1_5).typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); + assertTrue(l2.router.isOffRamp(l1.chainSelector, l2.EVM2EVMOffRamp1_2)); + assertTrue(l2.router.isOffRamp(l1.chainSelector, l2.EVM2EVMOffRamp1_5)); _label(); } @@ -209,8 +226,7 @@ contract ForkPoolAfterMigration is ForkBase { function test_RevertSendLegacyPool() public { uint256 amount = 10e18; Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); - message.tokenAmounts[0].token = address(l1.token); - message.tokenAmounts[0].amount = amount; + message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); vm.selectFork(l1.forkId); uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index fb4cbf60d4..16bc8770a3 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -30,13 +30,12 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { l2.tokenPool.setLegacyOnRamp(l1.chainSelector, l2.proxyPool); } - function test_sendAndReceiveFromL1() public { + function test_sendFromLegacyRouterL1() public { vm.selectFork(l1.forkId); uint256 amount = 10e18; Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); - message.tokenAmounts[0].token = address(l1.token); - message.tokenAmounts[0].amount = amount; + message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); @@ -46,6 +45,46 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); } + function test_releaseOrMintFrom1_2OffRamp() public { + uint256 amount = 10e18; + { + vm.selectFork(l1.forkId); + uint256 balanceBefore = l1.token.balanceOf(alice); + // mock release on legacy offramp + vm.prank(l1.EVM2EVMOffRamp1_2); + l1.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l2.chainSelector, ""); + assertEq(l1.token.balanceOf(alice), balanceBefore + amount); + } + { + vm.selectFork(l2.forkId); + uint256 balanceBefore = l2.token.balanceOf(alice); + // mock release on legacy offramp + vm.prank(l2.EVM2EVMOffRamp1_2); + l2.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l1.chainSelector, ""); + assertEq(l2.token.balanceOf(alice), balanceBefore + amount); + } + } + + function test_releaseOrMintFrom1_5OffRamp() public { + uint256 amount = 10e18; + { + vm.selectFork(l1.forkId); + uint256 balanceBefore = l1.token.balanceOf(alice); + // mock release on legacy offramp + vm.prank(l1.EVM2EVMOffRamp1_5); + l1.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l2.chainSelector, ""); + assertEq(l1.token.balanceOf(alice), balanceBefore + amount); + } + { + vm.selectFork(l2.forkId); + uint256 balanceBefore = l2.token.balanceOf(alice); + // mock release on legacy offramp + vm.prank(l2.EVM2EVMOffRamp1_5); + l2.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l1.chainSelector, ""); + assertEq(l2.token.balanceOf(alice), balanceBefore + amount); + } + } + function _upgradeExistingLockReleaseTokenPool() internal { UpgradeableLockReleaseTokenPool_Sepolia poolImpl = new UpgradeableLockReleaseTokenPool_Sepolia( address(l1.token), From b8f6ce08c986c63948002d2593eda685e6fb3f26 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 19:14:02 +0530 Subject: [PATCH 26/53] fix: legacyOnRamp -> proxyPool --- .../ccip/pools/GHO/UpgradeableTokenPool.sol | 35 ++++++++++--------- .../test/pools/GHO/GhoTokenPoolRemote.t.sol | 22 ++++++------ .../TokenPoolsUpgrade.t.sol | 6 ++-- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 8534ad45b8..a34df53da2 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -55,11 +55,12 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain } - /// @dev The storage slot for the legacy onRamp address. + /// @dev The storage slot for Proxy Pool address, act as an on ramp "wrapper" post ccip 1.5 migration. /// @dev This was added to continue support for 1.2 onRamp during 1.5 migration, and is stored /// this way to avoid storage collision. - bytes32 internal constant LEGACY_ON_RAMP_STORAGE_SLOT = - 0x1502550b48234eae00e6e40fbe138bacb20fb810a4e848e89695d938ac3530a6; // bytes32(uint256(keccak256("ccip.pools.GHO.UpgradeableTokenPool.legacyOnRamp")) - 1) + // bytes32(uint256(keccak256("ccip.pools.GHO.UpgradeableTokenPool.proxyPool")) - 1) + bytes32 internal constant PROXY_POOL_SLOT = 0x75bb68f1b335d4dab6963140ecff58281174ef4362bb85a8593ab9379f24fae2; + /// @dev The bridgeable token that is managed by this pool. IERC20 internal immutable i_token; /// @dev The address of the arm proxy @@ -255,7 +256,7 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (msg.sender != s_router.getOnRamp(remoteChainSelector) && msg.sender != getLegacyOnRamp(remoteChainSelector)) + if (msg.sender != s_router.getOnRamp(remoteChainSelector) && msg.sender != getProxyPool(remoteChainSelector)) revert CallerIsNotARampOnRouter(msg.sender); _; } @@ -324,27 +325,27 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { _; } - /// @notice Getter for legacy onRamp address. - /// @param remoteChainSelector The remote chain selector for which the legacy onRamp is being retrieved. - /// @return legacyOnRamp The legacy onRamp address for the given remoteChainSelector - function getLegacyOnRamp(uint64 remoteChainSelector) public view returns (address legacyOnRamp) { + /// @notice Getter for proxy pool address. + /// @param remoteChainSelector The remote chain selector for which the proxy pool is being retrieved. + /// @return proxyPool The proxy pool address for the given remoteChainSelector + function getProxyPool(uint64 remoteChainSelector) public view returns (address proxyPool) { assembly ("memory-safe") { - mstore(0, LEGACY_ON_RAMP_STORAGE_SLOT) + mstore(0, PROXY_POOL_SLOT) mstore(32, remoteChainSelector) - legacyOnRamp := shr(96, shl(96, sload(keccak256(0, 64)))) + proxyPool := shr(96, shl(96, sload(keccak256(0, 64)))) } } - /// @notice Setter for legacy onRamp address, only callable by the DAO. - /// @param remoteChainSelector The remote chain selector for which the legacy onRamp is being set. - /// @param legacyOnRamp The address of the legacy onRamp. - function setLegacyOnRamp(uint64 remoteChainSelector, address legacyOnRamp) external onlyOwner { + /// @notice Setter for proxy pool address, only callable by the DAO. + /// @param remoteChainSelector The remote chain selector for which the proxy pool is being set. + /// @param proxyPool The address of the proxy pool. + function setProxyPool(uint64 remoteChainSelector, address proxyPool) external onlyOwner { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - // todo: add check for typeAndVersion or getStaticConfig().destChainSelector on legacyOnRamp? + // todo: add check for typeAndVersion, immutable? assembly ("memory-safe") { - mstore(0, LEGACY_ON_RAMP_STORAGE_SLOT) + mstore(0, PROXY_POOL_SLOT) mstore(32, remoteChainSelector) - sstore(keccak256(0, 64), legacyOnRamp) + sstore(keccak256(0, 64), proxyPool) } } } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index 975c7ab5a9..eeb7eaa87f 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -377,28 +377,28 @@ contract GhoTokenPoolRemote_setRateLimitAdmin is GhoTokenPoolRemoteSetup { } } -contract GhoTokenPoolRemote_legacyOnRamp is GhoTokenPoolRemoteSetup { - function testSetLegacyOnRampAdminReverts() public { +contract GhoTokenPoolRemote_proxyPool is GhoTokenPoolRemoteSetup { + function testSetProxyPoolAdminReverts() public { vm.startPrank(STRANGER); vm.expectRevert("Only callable by owner"); - s_pool.setLegacyOnRamp(DEST_CHAIN_SELECTOR, makeAddr("legacyOnRamp")); + s_pool.setProxyPool(DEST_CHAIN_SELECTOR, makeAddr("proxyPool")); } - function testSetLegacyOnRampInvalidChainReverts(uint64 nonExistentChainSelector) public { + function testSetProxyPoolInvalidChainReverts(uint64 nonExistentChainSelector) public { vm.assume(nonExistentChainSelector != DEST_CHAIN_SELECTOR); changePrank(AAVE_DAO); vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ChainNotAllowed.selector, nonExistentChainSelector)); - s_pool.setLegacyOnRamp(nonExistentChainSelector, makeAddr("legacyOnRamp")); + s_pool.setProxyPool(nonExistentChainSelector, makeAddr("proxyPool")); } - function testSetLegacyOnRampSuccess(address legacyOnRamp) public { + function testSetProxyPoolSuccess(address proxyPool) public { changePrank(AAVE_DAO); - s_pool.setLegacyOnRamp(DEST_CHAIN_SELECTOR, legacyOnRamp); + s_pool.setProxyPool(DEST_CHAIN_SELECTOR, proxyPool); - assertEq(s_pool.getLegacyOnRamp(DEST_CHAIN_SELECTOR), legacyOnRamp); + assertEq(s_pool.getProxyPool(DEST_CHAIN_SELECTOR), proxyPool); } - function testFuzzGetLegacyOnRamp(uint64 chainSelector, address legacyOnRamp) public { + function testFuzzGetProxyPool(uint64 chainSelector, address proxyPool) public { vm.assume(chainSelector != DEST_CHAIN_SELECTOR); UpgradeableTokenPool.ChainUpdate[] memory chains = new UpgradeableTokenPool.ChainUpdate[](1); chains[0] = UpgradeableTokenPool.ChainUpdate({ @@ -410,8 +410,8 @@ contract GhoTokenPoolRemote_legacyOnRamp is GhoTokenPoolRemoteSetup { changePrank(AAVE_DAO); s_pool.applyChainUpdates(chains); // more robust than modifying `s_remoteChainSelectors` set storage - s_pool.setLegacyOnRamp(chainSelector, legacyOnRamp); + s_pool.setProxyPool(chainSelector, proxyPool); - assertEq(s_pool.getLegacyOnRamp(chainSelector), legacyOnRamp); + assertEq(s_pool.getProxyPool(chainSelector), proxyPool); } } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index 16bc8770a3..d90dabfb44 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -20,14 +20,14 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { vm.selectFork(l2.forkId); _upgradeExistingBurnMintTokenPool(); - // #2: update legacyOnRamp + // #2: setProxyPool vm.selectFork(l1.forkId); vm.prank(l1.tokenPool.owner()); - l1.tokenPool.setLegacyOnRamp(l2.chainSelector, l1.proxyPool); + l1.tokenPool.setProxyPool(l2.chainSelector, l1.proxyPool); vm.selectFork(l2.forkId); vm.prank(l2.tokenPool.owner()); - l2.tokenPool.setLegacyOnRamp(l1.chainSelector, l2.proxyPool); + l2.tokenPool.setProxyPool(l1.chainSelector, l2.proxyPool); } function test_sendFromLegacyRouterL1() public { From 6278e91bf61578dae457abb37e4107de1ef5a341 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 19:51:56 +0530 Subject: [PATCH 27/53] feat: set proxy pool only once for dest chain --- .../v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 7 ++++++- .../ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index a34df53da2..2cd98ba1fb 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -28,6 +28,7 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { error ChainNotAllowed(uint64 remoteChainSelector); error BadARMSignal(); error ChainAlreadyExists(uint64 chainSelector); + error ProxyPoolAlreadySet(uint64 remoteChainSelector); event Locked(address indexed sender, uint256 amount); event Burned(address indexed sender, uint256 amount); @@ -340,8 +341,12 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// @param remoteChainSelector The remote chain selector for which the proxy pool is being set. /// @param proxyPool The address of the proxy pool. function setProxyPool(uint64 remoteChainSelector, address proxyPool) external onlyOwner { + _setPoolProxy(remoteChainSelector, proxyPool); + } + + function _setPoolProxy(uint64 remoteChainSelector, address proxyPool) internal { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - // todo: add check for typeAndVersion, immutable? + if (getProxyPool(remoteChainSelector) != address(0)) revert ProxyPoolAlreadySet(remoteChainSelector); assembly ("memory-safe") { mstore(0, PROXY_POOL_SLOT) mstore(32, remoteChainSelector) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index eeb7eaa87f..cb367a5e7f 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -414,4 +414,18 @@ contract GhoTokenPoolRemote_proxyPool is GhoTokenPoolRemoteSetup { assertEq(s_pool.getProxyPool(chainSelector), proxyPool); } + + function testSetProxyPoolOnlyOnce(address newProxyPool) public { + changePrank(AAVE_DAO); + address proxyPool = makeAddr("proxyPool"); + s_pool.setProxyPool(DEST_CHAIN_SELECTOR, proxyPool); + + vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ProxyPoolAlreadySet.selector, DEST_CHAIN_SELECTOR)); + s_pool.setProxyPool(DEST_CHAIN_SELECTOR, proxyPool); + + vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ProxyPoolAlreadySet.selector, DEST_CHAIN_SELECTOR)); + s_pool.setProxyPool(DEST_CHAIN_SELECTOR, newProxyPool); + + assertEq(s_pool.getProxyPool(DEST_CHAIN_SELECTOR), proxyPool); + } } From c346fee0ca3cfd1506e92ab4926b17ecf6a0e684 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 20:09:00 +0530 Subject: [PATCH 28/53] feat(tokenPools): disableInitializer on constructor, upd docs --- .../v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol | 8 +++++++- .../ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 33c42760b7..6e80b9d020 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -19,6 +19,10 @@ import {IRouter} from "../../interfaces/IRouter.sol"; /// - Implementation of Initializable to allow upgrades /// - Move of allowlist and router definition to initialization stage /// - Inclusion of rate limit admin who may configure rate limits in addition to owner +/// - Modifications from inherited contracts +/// - UpgradeableTokenPool +/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) +/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { error Unauthorized(address caller); @@ -36,7 +40,9 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken address token, address armProxy, bool allowlistEnabled - ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} + ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) { + _disableInitializers(); + } /// @dev Initializer /// @dev The address passed as `owner` must accept ownership after initialization. diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index 8001ae59fa..85a357c67d 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -21,6 +21,10 @@ import {IRouter} from "../../interfaces/IRouter.sol"; /// - Implementation of Initializable to allow upgrades /// - Move of allowlist and router definition to initialization stage /// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked) +/// - Modifications from inherited contracts +/// - UpgradeableTokenPool +/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) +/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, ILiquidityContainer, ITypeAndVersion { using SafeERC20 for IERC20; @@ -71,6 +75,7 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, bool acceptLiquidity ) UpgradeableTokenPool(IERC20(token), armProxy, allowlistEnabled) { i_acceptLiquidity = acceptLiquidity; + _disableInitializers(); } /// @dev Initializer From 29d6620c80e4f755352928ddda53806e6fd5a3a4 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 20:31:05 +0530 Subject: [PATCH 29/53] fix: revert UpgradeableBurnMintTokenPoolOld from #16 (was accidentally merged) --- .../GHO/UpgradeableBurnMintTokenPoolOld.sol | 56 ------------- .../ccip/test/pools/GHO/GhoBaseTest.t.sol | 31 ------- .../pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol | 81 ------------------- 3 files changed, 168 deletions(-) delete mode 100644 contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPoolOld.sol delete 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 deleted file mode 100644 index 16ab280431..0000000000 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPoolOld.sol +++ /dev/null @@ -1,56 +0,0 @@ -// 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 52c22fc4bd..66d6fc63b5 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,7 +8,6 @@ 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"; @@ -66,36 +65,6 @@ 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 deleted file mode 100644 index 763f6303bd..0000000000 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemoteUpgrade.t.sol +++ /dev/null @@ -1,81 +0,0 @@ -// 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)); - } -} From 4477e39e5278481310f97875842620d09bec0a70 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Wed, 16 Oct 2024 21:11:09 +0530 Subject: [PATCH 30/53] chore: update diffs --- .../UpgradeableBurnMintTokenPool_diff.md | 34 ++++---- .../UpgradeableLockReleaseTokenPool_diff.md | 57 +++++++------ .../GHO/diffs/UpgradeableTokenPool_diff.md | 80 +++++++++++++++++-- 3 files changed, 126 insertions(+), 45 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index 066847e4f8..0ea501feec 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -1,17 +1,17 @@ ```diff diff --git a/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -index 9af0f22f4c..a46ff915e5 100644 +index 9af0f22f4c..92f004ff04 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -@@ -1,28 +1,90 @@ +@@ -1,28 +1,103 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; - + -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; +import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; - + -import {TokenPool} from "./TokenPool.sol"; -import {BurnMintTokenPoolAbstract} from "./BurnMintTokenPoolAbstract.sol"; +import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; @@ -29,9 +29,14 @@ index 9af0f22f4c..a46ff915e5 100644 +/// @dev Contract adaptations: +/// - Implementation of Initializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage ++/// - Inclusion of rate limit admin who may configure rate limits in addition to owner ++/// - Modifications from inherited contracts ++/// - UpgradeableTokenPool ++/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) ++/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool +contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { + error Unauthorized(address caller); - + -/// @notice This pool mints and burns a 3rd-party token. -/// @dev Pool whitelisting mode is set in the constructor and cannot be modified later. -/// It either accepts any address as originalSender, or only accepts whitelisted originalSender. @@ -39,7 +44,7 @@ index 9af0f22f4c..a46ff915e5 100644 -/// If that is expected, please make sure the token's burner/minter roles are adjustable. -contract BurnMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion { string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; - + + /// @notice The address of the rate limiter admin. + /// @dev Can be address(0) if none is configured. + address internal s_rateLimitAdmin; @@ -56,9 +61,10 @@ index 9af0f22f4c..a46ff915e5 100644 - address router - ) TokenPool(token, allowlist, armProxy, router) {} + bool allowlistEnabled -+ ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} - -- /// @inheritdoc BurnMintTokenPoolAbstract ++ ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) { ++ _disableInitializers(); ++ } ++ + /// @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 @@ -66,8 +72,7 @@ index 9af0f22f4c..a46ff915e5 100644 + /// @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(); ++ if (owner == address(0) || router == address(0)) revert ZeroAddressNotAllowed(); + _transferOwnership(owner); + + s_router = IRouter(router); @@ -77,7 +82,7 @@ index 9af0f22f4c..a46ff915e5 100644 + _applyAllowListUpdates(new address[](0), allowlist); + } + } -+ +++ + /// @notice Sets the rate limiter admin address. + /// @dev Only callable by the owner. + /// @param rateLimitAdmin The new rate limiter admin address. @@ -90,7 +95,7 @@ index 9af0f22f4c..a46ff915e5 100644 + return s_rateLimitAdmin; + } + -+ /// @notice Sets the rate limiter admin address. ++ /// @notice Sets the chain rate limiter config. + /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal + /// onlyAdmin check in the base implementation to also allow the rate limiter admin. + /// @param remoteChainSelector The remote chain selector for which the rate limits apply. @@ -105,7 +110,8 @@ index 9af0f22f4c..a46ff915e5 100644 + + _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); + } -+ + +- /// @inheritdoc BurnMintTokenPoolAbstract + /// @inheritdoc UpgradeableBurnMintTokenPoolAbstract function _burn(uint256 amount) internal virtual override { IBurnMintERC20(address(i_token)).burn(amount); diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md index 1e738e3bdf..150cbbc484 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md @@ -1,27 +1,27 @@ ```diff diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -index 1a17fa0398..9a30b1e977 100644 +index 1a17fa0398..9676fd95f3 100644 --- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -@@ -1,26 +1,39 @@ +@@ -1,26 +1,43 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; - + -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {ILiquidityContainer} from "../../rebalancer/interfaces/ILiquidityContainer.sol"; +import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; - + -import {TokenPool} from "./TokenPool.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; +import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; +import {ILiquidityContainer} from "../../../rebalancer/interfaces/ILiquidityContainer.sol"; - + -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; - + -/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism. -/// Because of lock/unlock requiring liquidity, this pool contract also has function to add and remove -/// liquidity. This allows for proper bookkeeping for both user and liquidity provider balances. @@ -39,13 +39,17 @@ index 1a17fa0398..9a30b1e977 100644 +/// - Implementation of Initializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage +/// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked) ++/// - Modifications from inherited contracts ++/// - UpgradeableTokenPool ++/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) ++/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool +contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, ILiquidityContainer, ITypeAndVersion { using SafeERC20 for IERC20; - + error InsufficientLiquidity(); error LiquidityNotAccepted(); error Unauthorized(address caller); - + + error BridgeLimitExceeded(uint256 bridgeLimit); + error NotEnoughBridgedAmount(); + @@ -53,12 +57,12 @@ index 1a17fa0398..9a30b1e977 100644 + event BridgeLimitAdminUpdated(address indexed oldAdmin, address indexed newAdmin); + string public constant override typeAndVersion = "LockReleaseTokenPool 1.4.0"; - + /// @dev The unique lock release pool flag to signal through EIP 165. -@@ -37,16 +50,55 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -37,14 +54,61 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @dev Can be address(0) if none is configured. address internal s_rateLimitAdmin; - + + /// @notice Maximum amount of tokens that can be bridged to other chains + uint256 private s_bridgeLimit; + /// @notice Amount of tokens bridged (transferred out) @@ -85,8 +89,9 @@ index 1a17fa0398..9a30b1e977 100644 + bool acceptLiquidity + ) UpgradeableTokenPool(IERC20(token), armProxy, allowlistEnabled) { i_acceptLiquidity = acceptLiquidity; - } - ++ _disableInitializers(); ++ } ++ + /// @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 @@ -100,8 +105,7 @@ index 1a17fa0398..9a30b1e977 100644 + address router, + uint256 bridgeLimit + ) public virtual initializer { -+ if (owner == address(0)) revert ZeroAddressNotAllowed(); -+ if (router == address(0)) revert ZeroAddressNotAllowed(); ++ if (owner == address(0) || router == address(0)) revert ZeroAddressNotAllowed(); + _transferOwnership(owner); + + s_router = IRouter(router); @@ -112,11 +116,9 @@ index 1a17fa0398..9a30b1e977 100644 + } + s_bridgeLimit = bridgeLimit; + } -+ + /// @notice Locks the token in the pool - /// @param amount Amount to lock - /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised -@@ -66,6 +118,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -66,6 +130,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion whenHealthy returns (bytes memory) { @@ -126,7 +128,7 @@ index 1a17fa0398..9a30b1e977 100644 _consumeOutboundRateLimit(remoteChainSelector, amount); emit Locked(msg.sender, amount); return ""; -@@ -83,6 +138,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -83,6 +150,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion uint64 remoteChainSelector, bytes memory ) external virtual override onlyOffRamp(remoteChainSelector) whenHealthy { @@ -138,10 +140,10 @@ index 1a17fa0398..9a30b1e977 100644 _consumeInboundRateLimit(remoteChainSelector, amount); getToken().safeTransfer(receiver, amount); emit Released(msg.sender, receiver, amount); -@@ -120,11 +180,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -120,11 +192,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion s_rateLimitAdmin = rateLimitAdmin; } - + + /// @notice Sets the bridge limit, the maximum amount of tokens that can be bridged out + /// @dev Only callable by the owner or the bridge limit admin. + /// @dev Bridge limit changes should be carefully managed, specially when reducing below the current bridged amount @@ -178,7 +180,7 @@ index 1a17fa0398..9a30b1e977 100644 function getRateLimitAdmin() external view returns (address) { return s_rateLimitAdmin; } - + + /// @notice Gets the bridge limiter admin address. + function getBridgeLimitAdmin() external view returns (address) { + return s_bridgeLimitAdmin; @@ -187,4 +189,13 @@ index 1a17fa0398..9a30b1e977 100644 /// @notice Checks if the pool can accept liquidity. /// @return true if the pool can accept liquidity, false otherwise. function canAcceptLiquidity() external view returns (bool) { +@@ -151,7 +260,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion + emit LiquidityRemoved(msg.sender, amount); + } + +- /// @notice Sets the rate limiter admin address. ++ /// @notice Sets the chain rate limiter config. + /// @dev Only callable by the owner or the rate limiter admin. NOTE: overwrites the normal + /// onlyAdmin check in the base implementation to also allow the rate limiter admin. + /// @param remoteChainSelector The remote chain selector for which the rate limits apply. ``` diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index fcdc197580..1b8da5ea16 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,32 +1,32 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..ee359ac1f8 100644 +index b3571bb449..2cd98ba1fb 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; - + -import {IPool} from "../interfaces/pools/IPool.sol"; -import {IARM} from "../interfaces/IARM.sol"; -import {IRouter} from "../interfaces/IRouter.sol"; +import {IPool} from "../../interfaces/pools/IPool.sol"; +import {IARM} from "../../interfaces/IARM.sol"; +import {IRouter} from "../../interfaces/IRouter.sol"; - + -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; +import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; - + -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; -import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; - + /// @notice Base abstract class with common functions for all token pools. /// A token pool serves as isolated place for holding tokens and token specific logic /// that may execute as tokens move across the bridge. @@ -35,7 +35,28 @@ index b3571bb449..ee359ac1f8 100644 using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; using RateLimiter for RateLimiter.TokenBucket; -@@ -74,23 +74,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -28,6 +28,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { + error ChainNotAllowed(uint64 remoteChainSelector); + error BadARMSignal(); + error ChainAlreadyExists(uint64 chainSelector); ++ error ProxyPoolAlreadySet(uint64 remoteChainSelector); + + event Locked(address indexed sender, uint256 amount); + event Burned(address indexed sender, uint256 amount); +@@ -55,6 +56,12 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { + RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain + } + ++ /// @dev The storage slot for Proxy Pool address, act as an on ramp "wrapper" post ccip 1.5 migration. ++ /// @dev This was added to continue support for 1.2 onRamp during 1.5 migration, and is stored ++ /// this way to avoid storage collision. ++ // bytes32(uint256(keccak256("ccip.pools.GHO.UpgradeableTokenPool.proxyPool")) - 1) ++ bytes32 internal constant PROXY_POOL_SLOT = 0x75bb68f1b335d4dab6963140ecff58281174ef4362bb85a8593ab9379f24fae2; ++ + /// @dev The bridgeable token that is managed by this pool. + IERC20 internal immutable i_token; + /// @dev The address of the arm proxy +@@ -74,23 +81,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { EnumerableSet.UintSet internal s_remoteChainSelectors; /// @dev Outbound rate limits. Corresponds to the inbound rate limit for the pool /// on the remote chain. @@ -46,7 +67,7 @@ index b3571bb449..ee359ac1f8 100644 /// degrees and prefer different limits) - mapping(uint64 remoteChainSelector => RateLimiter.TokenBucket) internal s_inboundRateLimits; + mapping(uint64 => RateLimiter.TokenBucket) internal s_inboundRateLimits; - + - constructor(IERC20 token, address[] memory allowlist, address armProxy, address router) { - if (address(token) == address(0) || router == address(0)) revert ZeroAddressNotAllowed(); + constructor(IERC20 token, address armProxy, bool allowlistEnabled) { @@ -62,6 +83,49 @@ index b3571bb449..ee359ac1f8 100644 - } + i_allowlistEnabled = allowlistEnabled; } - + /// @notice Get ARM proxy address +@@ -256,7 +257,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { + /// is a permissioned onRamp for the given chain on the Router. + modifier onlyOnRamp(uint64 remoteChainSelector) { + if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); +- if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); ++ if (msg.sender != s_router.getOnRamp(remoteChainSelector) && msg.sender != getProxyPool(remoteChainSelector)) ++ revert CallerIsNotARampOnRouter(msg.sender); + _; + } + +@@ -323,4 +325,32 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { + if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); + _; + } ++ ++ /// @notice Getter for proxy pool address. ++ /// @param remoteChainSelector The remote chain selector for which the proxy pool is being retrieved. ++ /// @return proxyPool The proxy pool address for the given remoteChainSelector ++ function getProxyPool(uint64 remoteChainSelector) public view returns (address proxyPool) { ++ assembly ("memory-safe") { ++ mstore(0, PROXY_POOL_SLOT) ++ mstore(32, remoteChainSelector) ++ proxyPool := shr(96, shl(96, sload(keccak256(0, 64)))) ++ } ++ } ++ ++ /// @notice Setter for proxy pool address, only callable by the DAO. ++ /// @param remoteChainSelector The remote chain selector for which the proxy pool is being set. ++ /// @param proxyPool The address of the proxy pool. ++ function setProxyPool(uint64 remoteChainSelector, address proxyPool) external onlyOwner { ++ _setPoolProxy(remoteChainSelector, proxyPool); ++ } ++ ++ function _setPoolProxy(uint64 remoteChainSelector, address proxyPool) internal { ++ if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); ++ if (getProxyPool(remoteChainSelector) != address(0)) revert ProxyPoolAlreadySet(remoteChainSelector); ++ assembly ("memory-safe") { ++ mstore(0, PROXY_POOL_SLOT) ++ mstore(32, remoteChainSelector) ++ sstore(keccak256(0, 64), proxyPool) ++ } ++ } + } ``` From 6fd10bb3216bce0221ca51b2025568a0159ecd58 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:39:15 +0530 Subject: [PATCH 31/53] chore: use negative conditional code style --- contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 2cd98ba1fb..7b268b2f74 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -257,7 +257,7 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (msg.sender != s_router.getOnRamp(remoteChainSelector) && msg.sender != getProxyPool(remoteChainSelector)) + if (!(msg.sender == getProxyPool(remoteChainSelector) || msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); _; } From 327fde2701075d57e2e59fd07ba114bf3eaf0a32 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:55:37 +0530 Subject: [PATCH 32/53] docs: move base contract modification doc --- .../GHO/UpgradeableBurnMintTokenPool.sol | 9 ++-- .../GHO/UpgradeableLockReleaseTokenPool.sol | 9 ++-- .../ccip/pools/GHO/UpgradeableTokenPool.sol | 9 ++-- .../UpgradeableBurnMintTokenPool_diff.md | 12 ++--- .../GHO/diffs/UpgradeableTokenPool_diff.md | 54 +++++++++++-------- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 6e80b9d020..387f9375db 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -19,10 +19,8 @@ import {IRouter} from "../../interfaces/IRouter.sol"; /// - Implementation of Initializable to allow upgrades /// - Move of allowlist and router definition to initialization stage /// - Inclusion of rate limit admin who may configure rate limits in addition to owner -/// - Modifications from inherited contracts -/// - UpgradeableTokenPool -/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) -/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool +/// - Modifications from inherited contract (see contract for more details): +/// - UpgradeableTokenPool: Modify `onlyOnRamp` modifier to accept transactions from ProxyPool contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { error Unauthorized(address caller); @@ -51,8 +49,7 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken /// @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(); + if (owner == address(0) || router == address(0)) revert ZeroAddressNotAllowed(); _transferOwnership(owner); s_router = IRouter(router); diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index 85a357c67d..06051e171d 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -21,10 +21,8 @@ import {IRouter} from "../../interfaces/IRouter.sol"; /// - Implementation of Initializable to allow upgrades /// - Move of allowlist and router definition to initialization stage /// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked) -/// - Modifications from inherited contracts -/// - UpgradeableTokenPool -/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) -/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool +/// - Modifications from inherited contract (see contract for more details): +/// - UpgradeableTokenPool: Modify `onlyOnRamp` modifier to accept transactions from ProxyPool contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, ILiquidityContainer, ITypeAndVersion { using SafeERC20 for IERC20; @@ -91,8 +89,7 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, address router, uint256 bridgeLimit ) public virtual initializer { - if (owner == address(0)) revert ZeroAddressNotAllowed(); - if (router == address(0)) revert ZeroAddressNotAllowed(); + if (owner == address(0) || router == address(0)) revert ZeroAddressNotAllowed(); _transferOwnership(owner); s_router = IRouter(router); diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 7b268b2f74..b0aa10016c 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -12,9 +12,12 @@ import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/tok import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; -/// @notice Base abstract class with common functions for all token pools. -/// A token pool serves as isolated place for holding tokens and token specific logic -/// that may execute as tokens move across the bridge. +/// @title UpgradeableTokenPool +/// @author Aave Labs +/// @notice Upgradeable version of Chainlink's CCIP TokenPool +/// @dev Contract adaptations: +/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) +/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index 0ea501feec..feeda4042d 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -index 9af0f22f4c..92f004ff04 100644 +index 9af0f22f4c..0a46d052d0 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -@@ -1,28 +1,103 @@ +@@ -1,28 +1,94 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; @@ -30,10 +30,8 @@ index 9af0f22f4c..92f004ff04 100644 +/// - Implementation of Initializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage +/// - Inclusion of rate limit admin who may configure rate limits in addition to owner -+/// - Modifications from inherited contracts -+/// - UpgradeableTokenPool -+/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) -+/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool ++/// - Modifications from inherited contract (see contract for more details): ++/// - UpgradeableTokenPool: Modify `onlyOnRamp` modifier to accept transactions from ProxyPool +contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintTokenPoolAbstract, ITypeAndVersion { + error Unauthorized(address caller); @@ -82,7 +80,7 @@ index 9af0f22f4c..92f004ff04 100644 + _applyAllowListUpdates(new address[](0), allowlist); + } + } -++ ++ + /// @notice Sets the rate limiter admin address. + /// @dev Only callable by the owner. + /// @param rateLimitAdmin The new rate limiter admin address. diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index 1b8da5ea16..d73a4f71ac 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,41 +1,51 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..2cd98ba1fb 100644 +index b3571bb449..b0aa10016c 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -@@ -1,21 +1,21 @@ +@@ -1,21 +1,24 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; -+pragma solidity ^0.8.0; - +- -import {IPool} from "../interfaces/pools/IPool.sol"; -import {IARM} from "../interfaces/IARM.sol"; -import {IRouter} from "../interfaces/IRouter.sol"; -+import {IPool} from "../../interfaces/pools/IPool.sol"; -+import {IARM} from "../../interfaces/IARM.sol"; -+import {IRouter} from "../../interfaces/IRouter.sol"; - +- -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; -+import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -+import {RateLimiter} from "../../libraries/RateLimiter.sol"; - +- -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; -import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; +- +-/// @notice Base abstract class with common functions for all token pools. +-/// A token pool serves as isolated place for holding tokens and token specific logic +-/// that may execute as tokens move across the bridge. +-abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { ++pragma solidity ^0.8.0; ++ ++import {IPool} from "../../interfaces/pools/IPool.sol"; ++import {IARM} from "../../interfaces/IARM.sol"; ++import {IRouter} from "../../interfaces/IRouter.sol"; ++ ++import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; ++import {RateLimiter} from "../../libraries/RateLimiter.sol"; ++ +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; - - /// @notice Base abstract class with common functions for all token pools. - /// A token pool serves as isolated place for holding tokens and token specific logic - /// that may execute as tokens move across the bridge. --abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { ++ ++/// @title UpgradeableTokenPool ++/// @author Aave Labs ++/// @notice Upgradeable version of Chainlink's CCIP TokenPool ++/// @dev Contract adaptations: ++/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) ++/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool +abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; using RateLimiter for RateLimiter.TokenBucket; -@@ -28,6 +28,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -28,6 +31,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { error ChainNotAllowed(uint64 remoteChainSelector); error BadARMSignal(); error ChainAlreadyExists(uint64 chainSelector); @@ -43,7 +53,7 @@ index b3571bb449..2cd98ba1fb 100644 event Locked(address indexed sender, uint256 amount); event Burned(address indexed sender, uint256 amount); -@@ -55,6 +56,12 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -55,6 +59,12 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain } @@ -56,7 +66,7 @@ index b3571bb449..2cd98ba1fb 100644 /// @dev The bridgeable token that is managed by this pool. IERC20 internal immutable i_token; /// @dev The address of the arm proxy -@@ -74,23 +81,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -74,23 +84,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { EnumerableSet.UintSet internal s_remoteChainSelectors; /// @dev Outbound rate limits. Corresponds to the inbound rate limit for the pool /// on the remote chain. @@ -85,17 +95,17 @@ index b3571bb449..2cd98ba1fb 100644 } /// @notice Get ARM proxy address -@@ -256,7 +257,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -256,7 +260,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); -+ if (msg.sender != s_router.getOnRamp(remoteChainSelector) && msg.sender != getProxyPool(remoteChainSelector)) ++ if (!(msg.sender == getProxyPool(remoteChainSelector) || msg.sender == s_router.getOnRamp(remoteChainSelector))) + revert CallerIsNotARampOnRouter(msg.sender); _; } -@@ -323,4 +325,32 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -323,4 +328,32 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); _; } From 59eda11bc9bee5ba19cee5aa4759105fb8921cf1 Mon Sep 17 00:00:00 2001 From: Dhairya Sethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:15:24 +0530 Subject: [PATCH 33/53] Update contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol Co-authored-by: miguelmtz <36620902+miguelmtzinf@users.noreply.github.com> --- .../pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 7df1b46488..47530d236a 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -223,7 +223,7 @@ contract ForkPoolAfterMigration is ForkBase { super.setUp(); } - function test_RevertSendLegacyPool() public { + function testSendViaLegacyPoolReverts() public { uint256 amount = 10e18; Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); From 2ee970366f49fd1a36c3f2aa8edca4418135e824 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:17:29 +0530 Subject: [PATCH 34/53] chore: update diffs --- .../UpgradeableLockReleaseTokenPool_diff.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md index 150cbbc484..c727b0b8cf 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -index 1a17fa0398..9676fd95f3 100644 +index 1a17fa0398..6da92b08cb 100644 --- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -@@ -1,26 +1,43 @@ +@@ -1,26 +1,41 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; @@ -39,10 +39,8 @@ index 1a17fa0398..9676fd95f3 100644 +/// - Implementation of Initializable to allow upgrades +/// - Move of allowlist and router definition to initialization stage +/// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked) -+/// - Modifications from inherited contracts -+/// - UpgradeableTokenPool -+/// - Setters & Getters for new ProxyPool (to support 1.5 CCIP migration on the existing 1.4 Pool) -+/// - Modify `onlyOnRamp` modifier to accept transactions from ProxyPool ++/// - Modifications from inherited contract (see contract for more details): ++/// - UpgradeableTokenPool: Modify `onlyOnRamp` modifier to accept transactions from ProxyPool +contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, ILiquidityContainer, ITypeAndVersion { using SafeERC20 for IERC20; @@ -59,7 +57,7 @@ index 1a17fa0398..9676fd95f3 100644 string public constant override typeAndVersion = "LockReleaseTokenPool 1.4.0"; /// @dev The unique lock release pool flag to signal through EIP 165. -@@ -37,14 +54,61 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -37,14 +52,53 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @dev Can be address(0) if none is configured. address internal s_rateLimitAdmin; @@ -115,10 +113,10 @@ index 1a17fa0398..9676fd95f3 100644 + _applyAllowListUpdates(new address[](0), allowlist); + } + s_bridgeLimit = bridgeLimit; -+ } + } /// @notice Locks the token in the pool -@@ -66,6 +130,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -66,6 +120,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion whenHealthy returns (bytes memory) { @@ -128,7 +126,7 @@ index 1a17fa0398..9676fd95f3 100644 _consumeOutboundRateLimit(remoteChainSelector, amount); emit Locked(msg.sender, amount); return ""; -@@ -83,6 +150,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -83,6 +140,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion uint64 remoteChainSelector, bytes memory ) external virtual override onlyOffRamp(remoteChainSelector) whenHealthy { @@ -140,7 +138,7 @@ index 1a17fa0398..9676fd95f3 100644 _consumeInboundRateLimit(remoteChainSelector, amount); getToken().safeTransfer(receiver, amount); emit Released(msg.sender, receiver, amount); -@@ -120,11 +192,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -120,11 +182,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion s_rateLimitAdmin = rateLimitAdmin; } @@ -189,7 +187,7 @@ index 1a17fa0398..9676fd95f3 100644 /// @notice Checks if the pool can accept liquidity. /// @return true if the pool can accept liquidity, false otherwise. function canAcceptLiquidity() external view returns (bool) { -@@ -151,7 +260,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -151,7 +250,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion emit LiquidityRemoved(msg.sender, amount); } From 2d357cbc0d7a3c5781f912eba468c90a0fb1282f Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:26:22 +0530 Subject: [PATCH 35/53] doc: for SendViaLegacyPool test --- .../GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 47530d236a..1b91ddb918 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -223,22 +223,30 @@ contract ForkPoolAfterMigration is ForkBase { super.setUp(); } + /// @dev Tests current version of token pools do not work with legacy on-ramps post 1.5 CCIP Migration + /// Only lockOrBurn is incompatible post migration since the new proxyPool becomes a 'wrapped' router + /// for the existing token pool, releaseOrMint is still compatible with legacy on-ramps + /// see more: https://github.com/smartcontractkit/ccip/blob/11c275959902783a3c4eaddbfaa5ce5f8707e01f/contracts/src/v0.8/ccip/test/legacy/TokenPoolAndProxy.t.sol#L130-L192 function testSendViaLegacyPoolReverts() public { uint256 amount = 10e18; + // generate lockOrBurn message for lockRelease token pool on L1 Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); vm.selectFork(l1.forkId); uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + // validate send reverts with onRamp caller as proxyPool vm.prank(alice); vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, l1.proxyPool)); l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); vm.selectFork(l2.forkId); + // modify generated lockOrBurn message for burnMint tokenPool on L2 message.tokenAmounts[0].token = address(l2.token); feeTokenAmount = l2.router.getFee(l1.chainSelector, message); + // validate send reverts with onRamp caller as proxyPool vm.prank(alice); vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, l2.proxyPool)); l2.router.ccipSend{value: feeTokenAmount}(l1.chainSelector, message); From b82025c2fe82ad6806e8e3a9d9e5f6c2dd458dae Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:48:24 +0530 Subject: [PATCH 36/53] chore: reorder imports for consistency with code style --- .../GHO/UpgradeableBurnMintTokenPool.sol | 6 ++--- .../GHO/UpgradeableLockReleaseTokenPool.sol | 6 ++--- .../ccip/pools/GHO/UpgradeableTokenPool.sol | 10 ++++---- .../UpgradeableBurnMintTokenPool_diff.md | 10 ++++---- .../UpgradeableLockReleaseTokenPool_diff.md | 21 ++++++++-------- .../GHO/diffs/UpgradeableTokenPool_diff.md | 24 +++++++++---------- .../ForkBase.t.sol | 6 ++--- .../TokenPoolsUpgrade.t.sol | 6 ++--- 8 files changed, 39 insertions(+), 50 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 387f9375db..67568c2eac 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -5,12 +5,10 @@ import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initiali 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 {RateLimiter} from "../../libraries/RateLimiter.sol"; - import {IRouter} from "../../interfaces/IRouter.sol"; +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; +import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; /// @title UpgradeableBurnMintTokenPool /// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index 06051e171d..d807c1b87e 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -6,13 +6,11 @@ import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initiali import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; import {ILiquidityContainer} from "../../../rebalancer/interfaces/ILiquidityContainer.sol"; -import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; - import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; - +import {RateLimiter} from "../../libraries/RateLimiter.sol"; import {IRouter} from "../../interfaces/IRouter.sol"; +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; /// @title UpgradeableLockReleaseTokenPool /// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index b0aa10016c..2e82bef88e 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,16 +1,14 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; -import {IPool} from "../../interfaces/pools/IPool.sol"; -import {IARM} from "../../interfaces/IARM.sol"; -import {IRouter} from "../../interfaces/IRouter.sol"; - import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; - import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; +import {IPool} from "../../interfaces/pools/IPool.sol"; +import {IARM} from "../../interfaces/IARM.sol"; +import {IRouter} from "../../interfaces/IRouter.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; /// @title UpgradeableTokenPool /// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index feeda4042d..947e2d4bb5 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -index 9af0f22f4c..0a46d052d0 100644 +index 9af0f22f4c..67568c2eac 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -@@ -1,28 +1,94 @@ +@@ -1,28 +1,92 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; @@ -16,12 +16,10 @@ index 9af0f22f4c..0a46d052d0 100644 -import {BurnMintTokenPoolAbstract} from "./BurnMintTokenPoolAbstract.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 {RateLimiter} from "../../libraries/RateLimiter.sol"; -+ +import {IRouter} from "../../interfaces/IRouter.sol"; ++import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; ++import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; + +/// @title UpgradeableBurnMintTokenPool +/// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md index c727b0b8cf..e419c47bab 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -index 1a17fa0398..6da92b08cb 100644 +index 1a17fa0398..fa297dc765 100644 --- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -@@ -1,26 +1,41 @@ +@@ -1,26 +1,40 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; @@ -19,7 +19,8 @@ index 1a17fa0398..6da92b08cb 100644 -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; -+import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; ++import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; ++import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; -/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism. @@ -27,10 +28,8 @@ index 1a17fa0398..6da92b08cb 100644 -/// liquidity. This allows for proper bookkeeping for both user and liquidity provider balances. -/// @dev One token per LockReleaseTokenPool. -contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion { -+import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -+import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; -+ +import {IRouter} from "../../interfaces/IRouter.sol"; ++import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; + +/// @title UpgradeableLockReleaseTokenPool +/// @author Aave Labs @@ -57,7 +56,7 @@ index 1a17fa0398..6da92b08cb 100644 string public constant override typeAndVersion = "LockReleaseTokenPool 1.4.0"; /// @dev The unique lock release pool flag to signal through EIP 165. -@@ -37,14 +52,53 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -37,14 +51,53 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @dev Can be address(0) if none is configured. address internal s_rateLimitAdmin; @@ -116,7 +115,7 @@ index 1a17fa0398..6da92b08cb 100644 } /// @notice Locks the token in the pool -@@ -66,6 +120,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -66,6 +119,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion whenHealthy returns (bytes memory) { @@ -126,7 +125,7 @@ index 1a17fa0398..6da92b08cb 100644 _consumeOutboundRateLimit(remoteChainSelector, amount); emit Locked(msg.sender, amount); return ""; -@@ -83,6 +140,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -83,6 +139,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion uint64 remoteChainSelector, bytes memory ) external virtual override onlyOffRamp(remoteChainSelector) whenHealthy { @@ -138,7 +137,7 @@ index 1a17fa0398..6da92b08cb 100644 _consumeInboundRateLimit(remoteChainSelector, amount); getToken().safeTransfer(receiver, amount); emit Released(msg.sender, receiver, amount); -@@ -120,11 +182,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -120,11 +181,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion s_rateLimitAdmin = rateLimitAdmin; } @@ -187,7 +186,7 @@ index 1a17fa0398..6da92b08cb 100644 /// @notice Checks if the pool can accept liquidity. /// @return true if the pool can accept liquidity, false otherwise. function canAcceptLiquidity() external view returns (bool) { -@@ -151,7 +250,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -151,7 +249,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion emit LiquidityRemoved(msg.sender, amount); } diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index d73a4f71ac..ce86036173 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..b0aa10016c 100644 +index b3571bb449..2e82bef88e 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -@@ -1,21 +1,24 @@ +@@ -1,21 +1,22 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; - @@ -24,16 +24,14 @@ index b3571bb449..b0aa10016c 100644 -abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +pragma solidity ^0.8.0; + -+import {IPool} from "../../interfaces/pools/IPool.sol"; -+import {IARM} from "../../interfaces/IARM.sol"; -+import {IRouter} from "../../interfaces/IRouter.sol"; -+ +import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -+import {RateLimiter} from "../../libraries/RateLimiter.sol"; -+ +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; ++import {IPool} from "../../interfaces/pools/IPool.sol"; ++import {IARM} from "../../interfaces/IARM.sol"; ++import {IRouter} from "../../interfaces/IRouter.sol"; ++import {RateLimiter} from "../../libraries/RateLimiter.sol"; + +/// @title UpgradeableTokenPool +/// @author Aave Labs @@ -45,7 +43,7 @@ index b3571bb449..b0aa10016c 100644 using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; using RateLimiter for RateLimiter.TokenBucket; -@@ -28,6 +31,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -28,6 +29,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { error ChainNotAllowed(uint64 remoteChainSelector); error BadARMSignal(); error ChainAlreadyExists(uint64 chainSelector); @@ -53,7 +51,7 @@ index b3571bb449..b0aa10016c 100644 event Locked(address indexed sender, uint256 amount); event Burned(address indexed sender, uint256 amount); -@@ -55,6 +59,12 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -55,6 +57,12 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain } @@ -66,7 +64,7 @@ index b3571bb449..b0aa10016c 100644 /// @dev The bridgeable token that is managed by this pool. IERC20 internal immutable i_token; /// @dev The address of the arm proxy -@@ -74,23 +84,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -74,23 +82,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { EnumerableSet.UintSet internal s_remoteChainSelectors; /// @dev Outbound rate limits. Corresponds to the inbound rate limit for the pool /// on the remote chain. @@ -95,7 +93,7 @@ index b3571bb449..b0aa10016c 100644 } /// @notice Get ARM proxy address -@@ -256,7 +260,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -256,7 +258,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); @@ -105,7 +103,7 @@ index b3571bb449..b0aa10016c 100644 _; } -@@ -323,4 +328,32 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -323,4 +326,32 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); _; } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 1b91ddb918..606e632eb5 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -3,13 +3,13 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; import {IERC20} from "../../../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol"; -import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; +import {ITypeAndVersion} from "../../../../../../shared/interfaces/ITypeAndVersion.sol"; import {IRouterClient} from "../../../../../interfaces/IRouterClient.sol"; import {IRouter as IRouterBase} from "../../../../../interfaces/IRouter.sol"; import {Client} from "../../../../../libraries/Client.sol"; import {Internal} from "../../../../../libraries/Internal.sol"; -import {ITypeAndVersion} from "../../../../../../shared/interfaces/ITypeAndVersion.sol"; +import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol"; +import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; interface IRouter is IRouterClient, IRouterBase { function getWrappedNative() external view returns (address); diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index d90dabfb44..3449dd747a 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; -import {ForkBase} from "./ForkBase.t.sol"; -import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol"; -import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; import {TransparentUpgradeableProxy} from "solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol"; import {ProxyAdmin} from "solidity-utils/contracts/transparent-proxy/ProxyAdmin.sol"; import {Client} from "../../../../../libraries/Client.sol"; import {Internal} from "../../../../../libraries/Internal.sol"; +import {ForkBase} from "./ForkBase.t.sol"; +import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol"; +import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; contract ForkPoolUpgradeAfterMigration is ForkBase { function setUp() public override { From 60e78e8ed5a74b87d3bfe545ac0450b71dab7d6d Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:51:10 +0530 Subject: [PATCH 37/53] fix: rm immutability of pool proxy --- .../v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 2 -- .../ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol | 14 -------------- 2 files changed, 16 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 2e82bef88e..f1dccc2249 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -29,7 +29,6 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { error ChainNotAllowed(uint64 remoteChainSelector); error BadARMSignal(); error ChainAlreadyExists(uint64 chainSelector); - error ProxyPoolAlreadySet(uint64 remoteChainSelector); event Locked(address indexed sender, uint256 amount); event Burned(address indexed sender, uint256 amount); @@ -347,7 +346,6 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { function _setPoolProxy(uint64 remoteChainSelector, address proxyPool) internal { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (getProxyPool(remoteChainSelector) != address(0)) revert ProxyPoolAlreadySet(remoteChainSelector); assembly ("memory-safe") { mstore(0, PROXY_POOL_SLOT) mstore(32, remoteChainSelector) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index cb367a5e7f..eeb7eaa87f 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -414,18 +414,4 @@ contract GhoTokenPoolRemote_proxyPool is GhoTokenPoolRemoteSetup { assertEq(s_pool.getProxyPool(chainSelector), proxyPool); } - - function testSetProxyPoolOnlyOnce(address newProxyPool) public { - changePrank(AAVE_DAO); - address proxyPool = makeAddr("proxyPool"); - s_pool.setProxyPool(DEST_CHAIN_SELECTOR, proxyPool); - - vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ProxyPoolAlreadySet.selector, DEST_CHAIN_SELECTOR)); - s_pool.setProxyPool(DEST_CHAIN_SELECTOR, proxyPool); - - vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ProxyPoolAlreadySet.selector, DEST_CHAIN_SELECTOR)); - s_pool.setProxyPool(DEST_CHAIN_SELECTOR, newProxyPool); - - assertEq(s_pool.getProxyPool(DEST_CHAIN_SELECTOR), proxyPool); - } } From 5153d5da1960313b95218e97d170619c72a2b985 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:25:27 +0530 Subject: [PATCH 38/53] test: rename for consistency --- .../GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index 3449dd747a..6d142fc7e9 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -30,7 +30,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { l2.tokenPool.setProxyPool(l1.chainSelector, l2.proxyPool); } - function test_sendFromLegacyRouterL1() public { + function test_lockOrBurnViaLegacyRouterL1() public { vm.selectFork(l1.forkId); uint256 amount = 10e18; @@ -45,7 +45,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); } - function test_releaseOrMintFrom1_2OffRamp() public { + function test_releaseOrMintVia1_2OffRamp() public { uint256 amount = 10e18; { vm.selectFork(l1.forkId); @@ -65,7 +65,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { } } - function test_releaseOrMintFrom1_5OffRamp() public { + function test_releaseOrMintVia1_5OffRamp() public { uint256 amount = 10e18; { vm.selectFork(l1.forkId); From e398344c45d10ee950f2f58a71d4aec28f892f11 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:38:31 +0530 Subject: [PATCH 39/53] fix: use chain agnostic proxypool --- .../ccip/pools/GHO/UpgradeableTokenPool.sol | 24 ++++++-------- .../test/pools/GHO/GhoTokenPoolRemote.t.sol | 33 +++++-------------- .../TokenPoolsUpgrade.t.sol | 4 +-- 3 files changed, 20 insertions(+), 41 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index f1dccc2249..a8e91549a9 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -257,7 +257,7 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == getProxyPool(remoteChainSelector) || msg.sender == s_router.getOnRamp(remoteChainSelector))) + if (!(msg.sender == getProxyPool() || msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); _; } @@ -327,29 +327,25 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { } /// @notice Getter for proxy pool address. - /// @param remoteChainSelector The remote chain selector for which the proxy pool is being retrieved. /// @return proxyPool The proxy pool address for the given remoteChainSelector - function getProxyPool(uint64 remoteChainSelector) public view returns (address proxyPool) { + function getProxyPool() public view returns (address proxyPool) { assembly ("memory-safe") { - mstore(0, PROXY_POOL_SLOT) - mstore(32, remoteChainSelector) - proxyPool := shr(96, shl(96, sload(keccak256(0, 64)))) + proxyPool := shr(96, shl(96, sload(PROXY_POOL_SLOT))) } } /// @notice Setter for proxy pool address, only callable by the DAO. - /// @param remoteChainSelector The remote chain selector for which the proxy pool is being set. + /// @dev This router is currently set for the Eth/Arb lane, and this pool is not expected + /// to support any other lanes in the future - hence can be stored agnostic to chain selector. /// @param proxyPool The address of the proxy pool. - function setProxyPool(uint64 remoteChainSelector, address proxyPool) external onlyOwner { - _setPoolProxy(remoteChainSelector, proxyPool); + function setProxyPool(address proxyPool) external onlyOwner { + _setPoolProxy(proxyPool); } - function _setPoolProxy(uint64 remoteChainSelector, address proxyPool) internal { - if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); + function _setPoolProxy(address proxyPool) internal { + if (proxyPool == address(0)) revert ZeroAddressNotAllowed(); assembly ("memory-safe") { - mstore(0, PROXY_POOL_SLOT) - mstore(32, remoteChainSelector) - sstore(keccak256(0, 64), proxyPool) + sstore(PROXY_POOL_SLOT, proxyPool) } } } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol index eeb7eaa87f..deba3b745b 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolRemote.t.sol @@ -381,37 +381,20 @@ contract GhoTokenPoolRemote_proxyPool is GhoTokenPoolRemoteSetup { function testSetProxyPoolAdminReverts() public { vm.startPrank(STRANGER); vm.expectRevert("Only callable by owner"); - s_pool.setProxyPool(DEST_CHAIN_SELECTOR, makeAddr("proxyPool")); + s_pool.setProxyPool(makeAddr("proxyPool")); } - function testSetProxyPoolInvalidChainReverts(uint64 nonExistentChainSelector) public { - vm.assume(nonExistentChainSelector != DEST_CHAIN_SELECTOR); - changePrank(AAVE_DAO); - vm.expectRevert(abi.encodeWithSelector(UpgradeableTokenPool.ChainNotAllowed.selector, nonExistentChainSelector)); - s_pool.setProxyPool(nonExistentChainSelector, makeAddr("proxyPool")); + function testSetProxyPoolZeroAddressReverts() public { + vm.startPrank(AAVE_DAO); + vm.expectRevert(UpgradeableTokenPool.ZeroAddressNotAllowed.selector); + s_pool.setProxyPool(address(0)); } function testSetProxyPoolSuccess(address proxyPool) public { + vm.assume(proxyPool != address(0)); changePrank(AAVE_DAO); - s_pool.setProxyPool(DEST_CHAIN_SELECTOR, proxyPool); - - assertEq(s_pool.getProxyPool(DEST_CHAIN_SELECTOR), proxyPool); - } - - function testFuzzGetProxyPool(uint64 chainSelector, address proxyPool) public { - vm.assume(chainSelector != DEST_CHAIN_SELECTOR); - UpgradeableTokenPool.ChainUpdate[] memory chains = new UpgradeableTokenPool.ChainUpdate[](1); - chains[0] = UpgradeableTokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - allowed: true, - outboundRateLimiterConfig: getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: getInboundRateLimiterConfig() - }); - - changePrank(AAVE_DAO); - s_pool.applyChainUpdates(chains); // more robust than modifying `s_remoteChainSelectors` set storage - s_pool.setProxyPool(chainSelector, proxyPool); + s_pool.setProxyPool(proxyPool); - assertEq(s_pool.getProxyPool(chainSelector), proxyPool); + assertEq(s_pool.getProxyPool(), proxyPool); } } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index 6d142fc7e9..f363c728e5 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -23,11 +23,11 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { // #2: setProxyPool vm.selectFork(l1.forkId); vm.prank(l1.tokenPool.owner()); - l1.tokenPool.setProxyPool(l2.chainSelector, l1.proxyPool); + l1.tokenPool.setProxyPool(l1.proxyPool); vm.selectFork(l2.forkId); vm.prank(l2.tokenPool.owner()); - l2.tokenPool.setProxyPool(l1.chainSelector, l2.proxyPool); + l2.tokenPool.setProxyPool(l2.proxyPool); } function test_lockOrBurnViaLegacyRouterL1() public { From c8f7c376f2efcc81dd6f438b72106c6b2225a65f Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:02:23 +0530 Subject: [PATCH 40/53] fix: import on token pools to match chainlink style convention, upd diff --- .../GHO/UpgradeableBurnMintTokenPool.sol | 8 +-- .../GHO/UpgradeableLockReleaseTokenPool.sol | 8 +-- .../ccip/pools/GHO/UpgradeableTokenPool.sol | 10 ++-- .../UpgradeableBurnMintTokenPool_diff.md | 12 ++-- .../UpgradeableLockReleaseTokenPool_diff.md | 27 +++++---- .../GHO/diffs/UpgradeableTokenPool_diff.md | 55 ++++++++----------- 6 files changed, 55 insertions(+), 65 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 67568c2eac..a41429c77a 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -1,14 +1,14 @@ // 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 {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {IRouter} from "../../interfaces/IRouter.sol"; + import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; +import {IRouter} from "../../interfaces/IRouter.sol"; +import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; /// @title UpgradeableBurnMintTokenPool /// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index d807c1b87e..ccf5ff4282 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -1,16 +1,16 @@ // 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 {ILiquidityContainer} from "../../../rebalancer/interfaces/ILiquidityContainer.sol"; +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; + import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; import {IRouter} from "../../interfaces/IRouter.sol"; -import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; +import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; /// @title UpgradeableLockReleaseTokenPool /// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index a8e91549a9..24f893d3c7 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,15 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; -import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; -import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; import {IPool} from "../../interfaces/pools/IPool.sol"; import {IARM} from "../../interfaces/IARM.sol"; import {IRouter} from "../../interfaces/IRouter.sol"; + +import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; import {RateLimiter} from "../../libraries/RateLimiter.sol"; +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; + /// @title UpgradeableTokenPool /// @author Aave Labs /// @notice Upgradeable version of Chainlink's CCIP TokenPool diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index 947e2d4bb5..4c36dcf7a7 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -index 9af0f22f4c..67568c2eac 100644 +index 9af0f22f4c..a41429c77a 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -1,28 +1,92 @@ @@ -10,16 +10,16 @@ index 9af0f22f4c..67568c2eac 100644 -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; -+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 {TokenPool} from "./TokenPool.sol"; -import {BurnMintTokenPoolAbstract} from "./BurnMintTokenPoolAbstract.sol"; -+import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; -+import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; -+import {RateLimiter} from "../../libraries/RateLimiter.sol"; -+import {IRouter} from "../../interfaces/IRouter.sol"; +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; +import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; ++import {RateLimiter} from "../../libraries/RateLimiter.sol"; ++import {IRouter} from "../../interfaces/IRouter.sol"; ++import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; + +/// @title UpgradeableBurnMintTokenPool +/// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md index e419c47bab..a8111dd394 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md @@ -1,36 +1,35 @@ ```diff diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -index 1a17fa0398..fa297dc765 100644 +index 1a17fa0398..ccf5ff4282 100644 --- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -@@ -1,26 +1,40 @@ +@@ -1,26 +1,39 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {ILiquidityContainer} from "../../rebalancer/interfaces/ILiquidityContainer.sol"; -+import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; ++import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; ++import {ILiquidityContainer} from "../../../rebalancer/interfaces/ILiquidityContainer.sol"; -import {TokenPool} from "./TokenPool.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; -+import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; -+import {ILiquidityContainer} from "../../../rebalancer/interfaces/ILiquidityContainer.sol"; ++import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; ++import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; -+import {RateLimiter} from "../../libraries/RateLimiter.sol"; ++import {IRouter} from "../../interfaces/IRouter.sol"; ++import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; -/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism. -/// Because of lock/unlock requiring liquidity, this pool contract also has function to add and remove -/// liquidity. This allows for proper bookkeeping for both user and liquidity provider balances. -/// @dev One token per LockReleaseTokenPool. -contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion { -+import {IRouter} from "../../interfaces/IRouter.sol"; -+import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; -+ +/// @title UpgradeableLockReleaseTokenPool +/// @author Aave Labs +/// @notice Upgradeable version of Chainlink's CCIP LockReleaseTokenPool @@ -56,7 +55,7 @@ index 1a17fa0398..fa297dc765 100644 string public constant override typeAndVersion = "LockReleaseTokenPool 1.4.0"; /// @dev The unique lock release pool flag to signal through EIP 165. -@@ -37,14 +51,53 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -37,14 +50,53 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @dev Can be address(0) if none is configured. address internal s_rateLimitAdmin; @@ -115,7 +114,7 @@ index 1a17fa0398..fa297dc765 100644 } /// @notice Locks the token in the pool -@@ -66,6 +119,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -66,6 +118,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion whenHealthy returns (bytes memory) { @@ -125,7 +124,7 @@ index 1a17fa0398..fa297dc765 100644 _consumeOutboundRateLimit(remoteChainSelector, amount); emit Locked(msg.sender, amount); return ""; -@@ -83,6 +139,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -83,6 +138,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion uint64 remoteChainSelector, bytes memory ) external virtual override onlyOffRamp(remoteChainSelector) whenHealthy { @@ -137,7 +136,7 @@ index 1a17fa0398..fa297dc765 100644 _consumeInboundRateLimit(remoteChainSelector, amount); getToken().safeTransfer(receiver, amount); emit Released(msg.sender, receiver, amount); -@@ -120,11 +181,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -120,11 +180,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion s_rateLimitAdmin = rateLimitAdmin; } @@ -186,7 +185,7 @@ index 1a17fa0398..fa297dc765 100644 /// @notice Checks if the pool can accept liquidity. /// @return true if the pool can accept liquidity, false otherwise. function canAcceptLiquidity() external view returns (bool) { -@@ -151,7 +249,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -151,7 +248,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion emit LiquidityRemoved(msg.sender, amount); } diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index ce86036173..d4aeadefe4 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..2e82bef88e 100644 +index b3571bb449..24f893d3c7 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -@@ -1,21 +1,22 @@ +@@ -1,21 +1,24 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; - @@ -24,15 +24,17 @@ index b3571bb449..2e82bef88e 100644 -abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +pragma solidity ^0.8.0; + -+import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -+import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -+import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; -+import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; +import {IPool} from "../../interfaces/pools/IPool.sol"; +import {IARM} from "../../interfaces/IARM.sol"; +import {IRouter} from "../../interfaces/IRouter.sol"; ++ ++import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; + ++import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; ++import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; ++import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; ++ +/// @title UpgradeableTokenPool +/// @author Aave Labs +/// @notice Upgradeable version of Chainlink's CCIP TokenPool @@ -43,15 +45,7 @@ index b3571bb449..2e82bef88e 100644 using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; using RateLimiter for RateLimiter.TokenBucket; -@@ -28,6 +29,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { - error ChainNotAllowed(uint64 remoteChainSelector); - error BadARMSignal(); - error ChainAlreadyExists(uint64 chainSelector); -+ error ProxyPoolAlreadySet(uint64 remoteChainSelector); - - event Locked(address indexed sender, uint256 amount); - event Burned(address indexed sender, uint256 amount); -@@ -55,6 +57,12 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -55,6 +58,12 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain } @@ -64,7 +58,7 @@ index b3571bb449..2e82bef88e 100644 /// @dev The bridgeable token that is managed by this pool. IERC20 internal immutable i_token; /// @dev The address of the arm proxy -@@ -74,23 +82,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -74,23 +83,17 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { EnumerableSet.UintSet internal s_remoteChainSelectors; /// @dev Outbound rate limits. Corresponds to the inbound rate limit for the pool /// on the remote chain. @@ -93,46 +87,41 @@ index b3571bb449..2e82bef88e 100644 } /// @notice Get ARM proxy address -@@ -256,7 +258,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -256,7 +259,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); -+ if (!(msg.sender == getProxyPool(remoteChainSelector) || msg.sender == s_router.getOnRamp(remoteChainSelector))) ++ if (!(msg.sender == getProxyPool() || msg.sender == s_router.getOnRamp(remoteChainSelector))) + revert CallerIsNotARampOnRouter(msg.sender); _; } -@@ -323,4 +326,32 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -323,4 +327,27 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); _; } + + /// @notice Getter for proxy pool address. -+ /// @param remoteChainSelector The remote chain selector for which the proxy pool is being retrieved. + /// @return proxyPool The proxy pool address for the given remoteChainSelector -+ function getProxyPool(uint64 remoteChainSelector) public view returns (address proxyPool) { ++ function getProxyPool() public view returns (address proxyPool) { + assembly ("memory-safe") { -+ mstore(0, PROXY_POOL_SLOT) -+ mstore(32, remoteChainSelector) -+ proxyPool := shr(96, shl(96, sload(keccak256(0, 64)))) ++ proxyPool := shr(96, shl(96, sload(PROXY_POOL_SLOT))) + } + } + + /// @notice Setter for proxy pool address, only callable by the DAO. -+ /// @param remoteChainSelector The remote chain selector for which the proxy pool is being set. ++ /// @dev This router is currently set for the Eth/Arb lane, and this pool is not expected ++ /// to support any other lanes in the future - hence can be stored agnostic to chain selector. + /// @param proxyPool The address of the proxy pool. -+ function setProxyPool(uint64 remoteChainSelector, address proxyPool) external onlyOwner { -+ _setPoolProxy(remoteChainSelector, proxyPool); ++ function setProxyPool(address proxyPool) external onlyOwner { ++ _setPoolProxy(proxyPool); + } + -+ function _setPoolProxy(uint64 remoteChainSelector, address proxyPool) internal { -+ if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); -+ if (getProxyPool(remoteChainSelector) != address(0)) revert ProxyPoolAlreadySet(remoteChainSelector); ++ function _setPoolProxy(address proxyPool) internal { ++ if (proxyPool == address(0)) revert ZeroAddressNotAllowed(); + assembly ("memory-safe") { -+ mstore(0, PROXY_POOL_SLOT) -+ mstore(32, remoteChainSelector) -+ sstore(keccak256(0, 64), proxyPool) ++ sstore(PROXY_POOL_SLOT, proxyPool) + } + } } From 284cec3db1bf50fff362ce1de5c3e95de0732aa7 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:02:50 +0530 Subject: [PATCH 41/53] fix: test renaming to match style convention --- .../GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index f363c728e5..56c8e237be 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -30,7 +30,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { l2.tokenPool.setProxyPool(l2.proxyPool); } - function test_lockOrBurnViaLegacyRouterL1() public { + function testLockOrBurnViaLegacyRouterL1() public { vm.selectFork(l1.forkId); uint256 amount = 10e18; @@ -45,7 +45,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); } - function test_releaseOrMintVia1_2OffRamp() public { + function testReleaseOrMintVia1_2OffRamp() public { uint256 amount = 10e18; { vm.selectFork(l1.forkId); @@ -65,7 +65,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { } } - function test_releaseOrMintVia1_5OffRamp() public { + function testReleaseOrMintVia1_5OffRamp() public { uint256 amount = 10e18; { vm.selectFork(l1.forkId); From 2a40f63b0256afa8465df55a2c68c0d7098984a6 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:13:39 +0530 Subject: [PATCH 42/53] chore: fix import order --- .../src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol | 2 +- .../v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol | 2 +- .../ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md | 4 ++-- .../pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index a41429c77a..57a3226ef4 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -1,6 +1,7 @@ // 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"; @@ -8,7 +9,6 @@ import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; import {RateLimiter} from "../../libraries/RateLimiter.sol"; import {IRouter} from "../../interfaces/IRouter.sol"; -import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; /// @title UpgradeableBurnMintTokenPool /// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index ccf5ff4282..7de31fa91e 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -1,6 +1,7 @@ // 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 {ILiquidityContainer} from "../../../rebalancer/interfaces/ILiquidityContainer.sol"; @@ -10,7 +11,6 @@ import {RateLimiter} from "../../libraries/RateLimiter.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; import {IRouter} from "../../interfaces/IRouter.sol"; -import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; /// @title UpgradeableLockReleaseTokenPool /// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index 4c36dcf7a7..d7f14cdf40 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -index 9af0f22f4c..a41429c77a 100644 +index 9af0f22f4c..57a3226ef4 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -1,28 +1,92 @@ @@ -10,6 +10,7 @@ index 9af0f22f4c..a41429c77a 100644 -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; ++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"; @@ -19,7 +20,6 @@ index 9af0f22f4c..a41429c77a 100644 +import {UpgradeableBurnMintTokenPoolAbstract} from "./UpgradeableBurnMintTokenPoolAbstract.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; +import {IRouter} from "../../interfaces/IRouter.sol"; -+import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; + +/// @title UpgradeableBurnMintTokenPool +/// @author Aave Labs diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md index a8111dd394..d13b246408 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -index 1a17fa0398..ccf5ff4282 100644 +index 1a17fa0398..7de31fa91e 100644 --- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -1,26 +1,39 @@ @@ -10,6 +10,7 @@ index 1a17fa0398..ccf5ff4282 100644 -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {ILiquidityContainer} from "../../rebalancer/interfaces/ILiquidityContainer.sol"; ++import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; +import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; +import {ILiquidityContainer} from "../../../rebalancer/interfaces/ILiquidityContainer.sol"; @@ -23,7 +24,6 @@ index 1a17fa0398..ccf5ff4282 100644 +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IRouter} from "../../interfaces/IRouter.sol"; -+import {Initializable} from "solidity-utils/contracts/transparent-proxy/Initializable.sol"; -/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism. -/// Because of lock/unlock requiring liquidity, this pool contract also has function to add and remove From 1de6efbaef65b2f35d2cf222f122d48a7bc0d9d6 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:14:08 +0530 Subject: [PATCH 43/53] chore: rm internal inline, typo --- .../v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 7 ++----- .../pools/GHO/diffs/UpgradeableTokenPool_diff.md | 13 +++++-------- .../src/v0.8/ccip/test/pools/GHO/GhoBaseTest.t.sol | 12 ++++++------ 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 24f893d3c7..950b87a080 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -259,8 +259,9 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == getProxyPool() || msg.sender == s_router.getOnRamp(remoteChainSelector))) + if (!(msg.sender == getProxyPool() || msg.sender == s_router.getOnRamp(remoteChainSelector))) { revert CallerIsNotARampOnRouter(msg.sender); + } _; } @@ -341,10 +342,6 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// to support any other lanes in the future - hence can be stored agnostic to chain selector. /// @param proxyPool The address of the proxy pool. function setProxyPool(address proxyPool) external onlyOwner { - _setPoolProxy(proxyPool); - } - - function _setPoolProxy(address proxyPool) internal { if (proxyPool == address(0)) revert ZeroAddressNotAllowed(); assembly ("memory-safe") { sstore(PROXY_POOL_SLOT, proxyPool) diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index d4aeadefe4..ad92a8ddaf 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..24f893d3c7 100644 +index b3571bb449..950b87a080 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,21 +1,24 @@ @@ -87,17 +87,18 @@ index b3571bb449..24f893d3c7 100644 } /// @notice Get ARM proxy address -@@ -256,7 +259,8 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -256,7 +259,9 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned onRamp for the given chain on the Router. modifier onlyOnRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); -+ if (!(msg.sender == getProxyPool() || msg.sender == s_router.getOnRamp(remoteChainSelector))) ++ if (!(msg.sender == getProxyPool() || msg.sender == s_router.getOnRamp(remoteChainSelector))) { + revert CallerIsNotARampOnRouter(msg.sender); ++ } _; } -@@ -323,4 +327,27 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -323,4 +328,23 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); _; } @@ -115,10 +116,6 @@ index b3571bb449..24f893d3c7 100644 + /// to support any other lanes in the future - hence can be stored agnostic to chain selector. + /// @param proxyPool The address of the proxy pool. + function setProxyPool(address proxyPool) external onlyOwner { -+ _setPoolProxy(proxyPool); -+ } -+ -+ function _setPoolProxy(address proxyPool) internal { + if (proxyPool == address(0)) revert ZeroAddressNotAllowed(); + assembly ("memory-safe") { + sstore(PROXY_POOL_SLOT, proxyPool) 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..22f23697cc 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 @@ -51,7 +51,7 @@ abstract contract GhoBaseTest is BaseTest { emptyArray, router ); - TransparentUpgradeableProxy tokenPoolProxy = new TransparentUpgradeableProxy( + TransparentUpgradeableProxy tokenProxyPool = new TransparentUpgradeableProxy( address(tokenPoolImpl), proxyAdmin, tokenPoolInitParams @@ -59,10 +59,10 @@ abstract contract GhoBaseTest is BaseTest { // Manage ownership vm.stopPrank(); vm.prank(owner); - UpgradeableBurnMintTokenPool(address(tokenPoolProxy)).acceptOwnership(); + UpgradeableBurnMintTokenPool(address(tokenProxyPool)).acceptOwnership(); vm.startPrank(OWNER); - return address(tokenPoolProxy); + return address(tokenProxyPool); } function _deployUpgradeableLockReleaseTokenPool( @@ -83,7 +83,7 @@ abstract contract GhoBaseTest is BaseTest { router, bridgeLimit ); - TransparentUpgradeableProxy tokenPoolProxy = new TransparentUpgradeableProxy( + TransparentUpgradeableProxy tokenProxyPool = new TransparentUpgradeableProxy( address(tokenPoolImpl), proxyAdmin, tokenPoolInitParams @@ -92,10 +92,10 @@ abstract contract GhoBaseTest is BaseTest { // Manage ownership vm.stopPrank(); vm.prank(owner); - UpgradeableLockReleaseTokenPool(address(tokenPoolProxy)).acceptOwnership(); + UpgradeableLockReleaseTokenPool(address(tokenProxyPool)).acceptOwnership(); vm.startPrank(OWNER); - return address(tokenPoolProxy); + return address(tokenProxyPool); } function _inflateFacilitatorLevel(address tokenPool, address ghoToken, uint256 amount) internal { From 6bad8e963827ff616658b75fc25f7b4c332b7937 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:38:27 +0530 Subject: [PATCH 44/53] test: for 1_2OnRamp, refactor _messageToEvent --- .../ForkBase.t.sol | 90 +++++++++---------- .../TokenPoolsUpgrade.t.sol | 60 ++++++++++--- 2 files changed, 92 insertions(+), 58 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 606e632eb5..06abaf2f48 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -5,6 +5,8 @@ import {Test} from "forge-std/Test.sol"; import {IERC20} from "../../../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {ITypeAndVersion} from "../../../../../../shared/interfaces/ITypeAndVersion.sol"; import {IRouterClient} from "../../../../../interfaces/IRouterClient.sol"; +import {IEVM2AnyOnRamp} from "../../../../../interfaces/IEVM2AnyOnRamp.sol"; +import {IAny2EVMOffRamp} from "../../../../../interfaces/IAny2EVMOffRamp.sol"; import {IRouter as IRouterBase} from "../../../../../interfaces/IRouter.sol"; import {Client} from "../../../../../libraries/Client.sol"; import {Internal} from "../../../../../libraries/Internal.sol"; @@ -31,10 +33,10 @@ contract ForkBase is Test { UpgradeableLockReleaseTokenPool_Sepolia tokenPool; IRouter router; IERC20 token; - address EVM2EVMOnRamp1_2; - address EVM2EVMOnRamp1_5; - address EVM2EVMOffRamp1_2; - address EVM2EVMOffRamp1_5; + IEVM2AnyOnRamp EVM2EVMOnRamp1_2; + IEVM2AnyOnRamp EVM2EVMOnRamp1_5; + IAny2EVMOffRamp EVM2EVMOffRamp1_2; + IAny2EVMOffRamp EVM2EVMOffRamp1_5; address proxyPool; uint64 chainSelector; bytes32 metadataHash; @@ -44,10 +46,10 @@ contract ForkBase is Test { UpgradeableBurnMintTokenPool_ArbSepolia tokenPool; IRouter router; IERC20 token; - address EVM2EVMOnRamp1_2; - address EVM2EVMOnRamp1_5; - address EVM2EVMOffRamp1_2; - address EVM2EVMOffRamp1_5; + IEVM2AnyOnRamp EVM2EVMOnRamp1_2; + IEVM2AnyOnRamp EVM2EVMOnRamp1_5; + IAny2EVMOffRamp EVM2EVMOffRamp1_2; + IAny2EVMOffRamp EVM2EVMOffRamp1_5; address proxyPool; uint64 chainSelector; bytes32 metadataHash; @@ -69,10 +71,10 @@ contract ForkBase is Test { l1.router = IRouter(l1.tokenPool.getRouter()); l2.chainSelector = l1.tokenPool.getSupportedChains()[0]; l1.token = l1.tokenPool.getToken(); - l1.EVM2EVMOnRamp1_2 = 0x1f41c443Cf68750d5c195E2EA7051521d981fC77; // legacy on ramp - l1.EVM2EVMOnRamp1_5 = l1.router.getOnRamp(l2.chainSelector); - l1.EVM2EVMOffRamp1_2 = 0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1; - l1.EVM2EVMOffRamp1_5 = 0xD2f5edfD4561d6E7599F6c6888Bd353cAFd0c55E; + l1.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e); // legacy on ramp + l1.EVM2EVMOnRamp1_5 = IEVM2AnyOnRamp(l1.router.getOnRamp(l2.chainSelector)); + l1.EVM2EVMOffRamp1_2 = IAny2EVMOffRamp(0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1); + l1.EVM2EVMOffRamp1_5 = IAny2EVMOffRamp(0xD2f5edfD4561d6E7599F6c6888Bd353cAFd0c55E); vm.prank(alice); l1.token.approve(address(l1.router), type(uint256).max); deal(address(l1.token), alice, 1000e18); @@ -84,10 +86,10 @@ contract ForkBase is Test { l2.router = IRouter(l2.tokenPool.getRouter()); l1.chainSelector = l2.tokenPool.getSupportedChains()[0]; l2.token = l2.tokenPool.getToken(); - l2.EVM2EVMOnRamp1_2 = 0xc1eBd046A4086142479bE3Fc16A4791E2022909a; // legacy on ramp - l2.EVM2EVMOnRamp1_5 = l2.router.getOnRamp(l1.chainSelector); - l2.EVM2EVMOffRamp1_2 = 0x1c71f141b4630EBE52d6aF4894812960abE207eB; - l2.EVM2EVMOffRamp1_5 = 0xBed6e9131916d724418C8a6FE810F727302a5c00; + l2.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9); // legacy on ramp + l2.EVM2EVMOnRamp1_5 = IEVM2AnyOnRamp(l2.router.getOnRamp(l1.chainSelector)); + l2.EVM2EVMOffRamp1_2 = IAny2EVMOffRamp(0x1c71f141b4630EBE52d6aF4894812960abE207eB); + l2.EVM2EVMOffRamp1_5 = IAny2EVMOffRamp(0xBed6e9131916d724418C8a6FE810F727302a5c00); vm.prank(alice); l2.token.approve(address(l2.router), type(uint256).max); deal(address(l2.token), alice, 1000e18); @@ -102,12 +104,12 @@ contract ForkBase is Test { assertEq(l1.token.balanceOf(alice), 1000e18); assertEq(ITypeAndVersion(address(l1.router)).typeAndVersion(), "Router 1.2.0"); assertEq(ITypeAndVersion(l1.proxyPool).typeAndVersion(), "LockReleaseTokenPoolAndProxy 1.5.0"); - assertEq(ITypeAndVersion(l1.EVM2EVMOnRamp1_2).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); - assertEq(ITypeAndVersion(l1.EVM2EVMOnRamp1_5).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); - assertEq(ITypeAndVersion(l1.EVM2EVMOffRamp1_2).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); - assertEq(ITypeAndVersion(l1.EVM2EVMOffRamp1_5).typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); - assertTrue(l1.router.isOffRamp(l2.chainSelector, l1.EVM2EVMOffRamp1_2)); - assertTrue(l1.router.isOffRamp(l2.chainSelector, l1.EVM2EVMOffRamp1_5)); + assertEq(ITypeAndVersion(address(l1.EVM2EVMOnRamp1_2)).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(ITypeAndVersion(address(l1.EVM2EVMOnRamp1_5)).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); + assertEq(ITypeAndVersion(address(l1.EVM2EVMOffRamp1_2)).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); + assertEq(ITypeAndVersion(address(l1.EVM2EVMOffRamp1_5)).typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); + assertTrue(l1.router.isOffRamp(l2.chainSelector, address(l1.EVM2EVMOffRamp1_2))); + assertTrue(l1.router.isOffRamp(l2.chainSelector, address(l1.EVM2EVMOffRamp1_5))); vm.selectFork(l2.forkId); assertEq(l2.chainSelector, 3478487238524512106); @@ -115,12 +117,12 @@ contract ForkBase is Test { assertEq(l2.token.balanceOf(alice), 1000e18); assertEq(ITypeAndVersion(address(l2.router)).typeAndVersion(), "Router 1.2.0"); assertEq(ITypeAndVersion(l2.proxyPool).typeAndVersion(), "BurnMintTokenPoolAndProxy 1.5.0"); - assertEq(ITypeAndVersion(l2.EVM2EVMOnRamp1_2).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); - assertEq(ITypeAndVersion(l2.EVM2EVMOnRamp1_5).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); - assertEq(ITypeAndVersion(l2.EVM2EVMOffRamp1_2).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); - assertEq(ITypeAndVersion(l2.EVM2EVMOffRamp1_5).typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); - assertTrue(l2.router.isOffRamp(l1.chainSelector, l2.EVM2EVMOffRamp1_2)); - assertTrue(l2.router.isOffRamp(l1.chainSelector, l2.EVM2EVMOffRamp1_5)); + assertEq(ITypeAndVersion(address(l2.EVM2EVMOnRamp1_2)).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(ITypeAndVersion(address(l2.EVM2EVMOnRamp1_5)).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); + assertEq(ITypeAndVersion(address(l2.EVM2EVMOffRamp1_2)).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); + assertEq(ITypeAndVersion(address(l2.EVM2EVMOffRamp1_5)).typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); + assertTrue(l2.router.isOffRamp(l1.chainSelector, address(l2.EVM2EVMOffRamp1_2))); + assertTrue(l2.router.isOffRamp(l1.chainSelector, address(l2.EVM2EVMOffRamp1_5))); _label(); } @@ -141,17 +143,11 @@ contract ForkBase is Test { function _messageToEvent( Client.EVM2AnyMessage memory message, - uint64 seqNum, - uint64 nonce, + IEVM2AnyOnRamp onRamp, uint256 feeTokenAmount, address originalSender, - bytes32 metadataHash, - uint32 destGasAmount + bool isL1 ) public view returns (Internal.EVM2EVMMessage memory) { - address feeToken = metadataHash == l1.metadataHash ? l1.router.getWrappedNative() : l2.router.getWrappedNative(); - address destTokenAddress = metadataHash == l1.metadataHash ? address(l2.token) : address(l1.token); - address sourcePool = metadataHash == l1.metadataHash ? l1.proxyPool : l2.proxyPool; - // Slicing is only available for calldata. So we have to build a new bytes array. bytes memory args = new bytes(message.extraArgs.length - 4); for (uint256 i = 4; i < message.extraArgs.length; ++i) { @@ -159,18 +155,18 @@ contract ForkBase is Test { } Client.EVMExtraArgsV1 memory extraArgs = abi.decode(args, (Client.EVMExtraArgsV1)); Internal.EVM2EVMMessage memory messageEvent = Internal.EVM2EVMMessage({ - sequenceNumber: seqNum, + sequenceNumber: onRamp.getExpectedNextSequenceNumber(), feeTokenAmount: feeTokenAmount, sender: originalSender, - nonce: nonce, + nonce: onRamp.getSenderNonce(originalSender) + 1, gasLimit: extraArgs.gasLimit, strict: false, - sourceChainSelector: l1.chainSelector, + sourceChainSelector: isL1 ? l1.chainSelector : l2.chainSelector, receiver: abi.decode(message.receiver, (address)), data: message.data, tokenAmounts: message.tokenAmounts, sourceTokenData: new bytes[](message.tokenAmounts.length), - feeToken: feeToken, + feeToken: isL1 ? l1.router.getWrappedNative() : l2.router.getWrappedNative(), messageId: "" }); @@ -178,21 +174,21 @@ contract ForkBase is Test { // change introduced in 1.5 upgrade messageEvent.sourceTokenData[i] = abi.encode( SourceTokenData({ - sourcePoolAddress: abi.encode(sourcePool), - destTokenAddress: abi.encode(destTokenAddress), + sourcePoolAddress: abi.encode(isL1 ? l1.proxyPool : l2.proxyPool), + destTokenAddress: abi.encode(address(isL1 ? l2.token : l1.token)), extraData: "", - destGasAmount: destGasAmount + destGasAmount: 90000 }) ); } - messageEvent.messageId = Internal._hash(messageEvent, metadataHash); + messageEvent.messageId = Internal._hash(messageEvent, isL1 ? l1.metadataHash : l2.metadataHash); return messageEvent; } function _generateMetadataHash(uint64 sourceChainSelector) internal view returns (bytes32) { uint64 destChainSelector = sourceChainSelector == l1.chainSelector ? l2.chainSelector : l1.chainSelector; - address onRamp = sourceChainSelector == l1.chainSelector ? l1.EVM2EVMOnRamp1_5 : l2.EVM2EVMOnRamp1_5; + address onRamp = address(sourceChainSelector == l1.chainSelector ? l1.EVM2EVMOnRamp1_5 : l2.EVM2EVMOnRamp1_5); return keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, destChainSelector, onRamp)); } @@ -208,6 +204,8 @@ contract ForkBase is Test { vm.label(address(l1.proxyPool), "l1.proxyPool"); vm.label(address(l1.EVM2EVMOnRamp1_2), "l1.EVM2EVMOnRamp1_2"); vm.label(address(l1.EVM2EVMOnRamp1_5), "l1.EVM2EVMOnRamp1_5"); + vm.label(address(l1.EVM2EVMOffRamp1_2), "l1.EVM2EVMOffRamp1_2"); + vm.label(address(l1.EVM2EVMOffRamp1_5), "l1.EVM2EVMOffRamp1_5"); vm.label(address(l2.tokenPool), "l2.tokenPool"); vm.label(address(l2.token), "l2.token"); @@ -215,6 +213,8 @@ contract ForkBase is Test { vm.label(address(l2.proxyPool), "l2.proxyPool"); vm.label(address(l2.EVM2EVMOnRamp1_2), "l2.EVM2EVMOnRamp1_2"); vm.label(address(l2.EVM2EVMOnRamp1_5), "l2.EVM2EVMOnRamp1_5"); + vm.label(address(l2.EVM2EVMOffRamp1_2), "l2.EVM2EVMOffRamp1_2"); + vm.label(address(l2.EVM2EVMOffRamp1_5), "l2.EVM2EVMOffRamp1_5"); } } diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index 56c8e237be..11e2f12403 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -30,19 +30,53 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { l2.tokenPool.setProxyPool(l2.proxyPool); } - function testLockOrBurnViaLegacyRouterL1() public { - vm.selectFork(l1.forkId); - + function testLockOrBurnViaRouter() public { uint256 amount = 10e18; Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); - message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); - uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + { + vm.selectFork(l1.forkId); + + message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); + uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + + // router uses 1_5 onRamp + assertEq(l1.router.getOnRamp(l2.chainSelector), address(l1.EVM2EVMOnRamp1_5)); + vm.expectEmit(); + emit CCIPSendRequested(_messageToEvent(message, l1.EVM2EVMOnRamp1_5, feeTokenAmount, alice, true)); + vm.prank(alice); + l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); + } + + { + vm.selectFork(l2.forkId); + + message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l2.token), amount: amount}); + uint256 feeTokenAmount = l2.router.getFee(l1.chainSelector, message); - vm.expectEmit(); - emit CCIPSendRequested(_messageToEvent(message, 220, 1, feeTokenAmount, alice, l1.metadataHash, uint32(90000))); - vm.prank(alice); - l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); + // router uses 1_5 onRamp + assertEq(l2.router.getOnRamp(l1.chainSelector), address(l2.EVM2EVMOnRamp1_5)); + vm.expectEmit(); + emit CCIPSendRequested(_messageToEvent(message, l2.EVM2EVMOnRamp1_5, feeTokenAmount, alice, false)); + vm.prank(alice); + l2.router.ccipSend{value: feeTokenAmount}(l1.chainSelector, message); + } + } + + function testRevertLockOrBurnVia1_2OnRamp() public { + uint256 amount = 10e18; + { + vm.selectFork(l1.forkId); + vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, address(l1.EVM2EVMOnRamp1_2))); + vm.prank(address(l1.EVM2EVMOnRamp1_2)); + l1.tokenPool.lockOrBurn(alice, abi.encode(alice), amount, l2.chainSelector, ""); + } + { + vm.selectFork(l2.forkId); + vm.expectRevert(abi.encodeWithSelector(CallerIsNotARampOnRouter.selector, address(l2.EVM2EVMOnRamp1_2))); + vm.prank(address(l2.EVM2EVMOnRamp1_2)); + l2.tokenPool.lockOrBurn(alice, abi.encode(alice), amount, l1.chainSelector, ""); + } } function testReleaseOrMintVia1_2OffRamp() public { @@ -51,7 +85,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { vm.selectFork(l1.forkId); uint256 balanceBefore = l1.token.balanceOf(alice); // mock release on legacy offramp - vm.prank(l1.EVM2EVMOffRamp1_2); + vm.prank(address(l1.EVM2EVMOffRamp1_2)); l1.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l2.chainSelector, ""); assertEq(l1.token.balanceOf(alice), balanceBefore + amount); } @@ -59,7 +93,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { vm.selectFork(l2.forkId); uint256 balanceBefore = l2.token.balanceOf(alice); // mock release on legacy offramp - vm.prank(l2.EVM2EVMOffRamp1_2); + vm.prank(address(l2.EVM2EVMOffRamp1_2)); l2.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l1.chainSelector, ""); assertEq(l2.token.balanceOf(alice), balanceBefore + amount); } @@ -71,7 +105,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { vm.selectFork(l1.forkId); uint256 balanceBefore = l1.token.balanceOf(alice); // mock release on legacy offramp - vm.prank(l1.EVM2EVMOffRamp1_5); + vm.prank(address(l1.EVM2EVMOffRamp1_5)); l1.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l2.chainSelector, ""); assertEq(l1.token.balanceOf(alice), balanceBefore + amount); } @@ -79,7 +113,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { vm.selectFork(l2.forkId); uint256 balanceBefore = l2.token.balanceOf(alice); // mock release on legacy offramp - vm.prank(l2.EVM2EVMOffRamp1_5); + vm.prank(address(l2.EVM2EVMOffRamp1_5)); l2.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l1.chainSelector, ""); assertEq(l2.token.balanceOf(alice), balanceBefore + amount); } From cecb762142ade723f18804f09ae7dd9dd6a8d8a2 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:39:28 +0530 Subject: [PATCH 45/53] chore: rm unsued --- .../GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 06abaf2f48..4e48a16ded 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -192,11 +192,6 @@ contract ForkBase is Test { return keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, destChainSelector, onRamp)); } - function _selectForkAndStartPrank(uint forkId) internal { - vm.selectFork(forkId); - vm.startPrank(alice); - } - function _label() internal { vm.label(address(l1.tokenPool), "l1.tokenPool"); vm.label(address(l1.token), "l1.token"); From 623c10c203d5187eeb82a998ce3c01ea94bce6d9 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 17 Oct 2024 22:38:41 +0530 Subject: [PATCH 46/53] doc: rm incorrect comment on proxy pool --- contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 2 -- .../v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 950b87a080..4808aa3998 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -338,8 +338,6 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { } /// @notice Setter for proxy pool address, only callable by the DAO. - /// @dev This router is currently set for the Eth/Arb lane, and this pool is not expected - /// to support any other lanes in the future - hence can be stored agnostic to chain selector. /// @param proxyPool The address of the proxy pool. function setProxyPool(address proxyPool) external onlyOwner { if (proxyPool == address(0)) revert ZeroAddressNotAllowed(); diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index ad92a8ddaf..fa1012051c 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..950b87a080 100644 +index b3571bb449..4808aa3998 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,21 +1,24 @@ @@ -98,7 +98,7 @@ index b3571bb449..950b87a080 100644 _; } -@@ -323,4 +328,23 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -323,4 +328,21 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); _; } @@ -112,8 +112,6 @@ index b3571bb449..950b87a080 100644 + } + + /// @notice Setter for proxy pool address, only callable by the DAO. -+ /// @dev This router is currently set for the Eth/Arb lane, and this pool is not expected -+ /// to support any other lanes in the future - hence can be stored agnostic to chain selector. + /// @param proxyPool The address of the proxy pool. + function setProxyPool(address proxyPool) external onlyOwner { + if (proxyPool == address(0)) revert ZeroAddressNotAllowed(); From 459ffe869e8482a949577afe9adaaf212a143ce3 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Fri, 18 Oct 2024 18:00:50 +0530 Subject: [PATCH 47/53] test: fork test pre migration setup --- contracts/foundry.toml | 4 + .../ForkBase.t.sol | 148 +++++++++++++++++- 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/contracts/foundry.toml b/contracts/foundry.toml index c9b19de367..3ae4962578 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -16,6 +16,10 @@ gas_price = 1 block_timestamp = 1234567890 block_number = 12345 +[rpc_endpoints] +sepolia = "https://sepolia.gateway.tenderly.co" +arb_sepolia = "https://arbitrum-sepolia.gateway.tenderly.co" + [profile.ccip] solc_version = '0.8.19' src = 'src/v0.8/ccip' diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 4e48a16ded..d1240b613c 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -14,8 +14,13 @@ import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; interface IRouter is IRouterClient, IRouterBase { + struct OffRamp { + uint64 sourceChainSelector; + address offRamp; + } function getWrappedNative() external view returns (address); function isOffRamp(uint64, address) external view returns (bool); + function getOffRamps() external view returns (OffRamp[] memory); } struct SourceTokenData { @@ -61,9 +66,12 @@ contract ForkBase is Test { address internal alice = makeAddr("alice"); + uint256 internal constant BLOCK_AFTER_MIGRATION_L1 = 6884195; + uint256 internal constant BLOCK_AFTER_MIGRATION_L2 = 89058935; + function setUp() public virtual { - l1.forkId = vm.createFork("https://sepolia.gateway.tenderly.co", 6884195); - l2.forkId = vm.createFork("https://arbitrum-sepolia.gateway.tenderly.co", 89058935); + l1.forkId = vm.createFork(vm.rpcUrl("sepolia"), BLOCK_AFTER_MIGRATION_L1); + l2.forkId = vm.createFork(vm.rpcUrl("arb_sepolia"), BLOCK_AFTER_MIGRATION_L2); vm.selectFork(l1.forkId); l1.tokenPool = UpgradeableLockReleaseTokenPool_Sepolia(0x7768248E1Ff75612c18324bad06bb393c1206980); @@ -130,7 +138,7 @@ contract ForkBase is Test { function _generateMessage( address receiver, uint256 tokenAmountsLength - ) internal view returns (Client.EVM2AnyMessage memory) { + ) internal pure returns (Client.EVM2AnyMessage memory) { return Client.EVM2AnyMessage({ receiver: abi.encode(receiver), @@ -222,7 +230,7 @@ contract ForkPoolAfterMigration is ForkBase { /// Only lockOrBurn is incompatible post migration since the new proxyPool becomes a 'wrapped' router /// for the existing token pool, releaseOrMint is still compatible with legacy on-ramps /// see more: https://github.com/smartcontractkit/ccip/blob/11c275959902783a3c4eaddbfaa5ce5f8707e01f/contracts/src/v0.8/ccip/test/legacy/TokenPoolAndProxy.t.sol#L130-L192 - function testSendViaLegacyPoolReverts() public { + function testSendViaLegacyRouterReverts() public { uint256 amount = 10e18; // generate lockOrBurn message for lockRelease token pool on L1 Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); @@ -247,3 +255,135 @@ contract ForkPoolAfterMigration is ForkBase { l2.router.ccipSend{value: feeTokenAmount}(l1.chainSelector, message); } } + +contract ForkPoolBeforeMigration is ForkBase { + uint256 internal constant BLOCK_BEFORE_MIGRATION_L1 = 6673089; + uint256 internal constant BLOCK_BEFORE_MIGRATION_L2 = 79570677; + + function setUp() public override { + l1.forkId = vm.createFork(vm.rpcUrl("sepolia"), BLOCK_BEFORE_MIGRATION_L1); + l2.forkId = vm.createFork(vm.rpcUrl("arb_sepolia"), BLOCK_BEFORE_MIGRATION_L2); + + vm.selectFork(l1.forkId); + l1.tokenPool = UpgradeableLockReleaseTokenPool_Sepolia(0x7768248E1Ff75612c18324bad06bb393c1206980); + l1.router = IRouter(l1.tokenPool.getRouter()); + l2.chainSelector = l1.tokenPool.getSupportedChains()[0]; + l1.token = l1.tokenPool.getToken(); + l1.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(l1.router.getOnRamp(l2.chainSelector)); + l1.EVM2EVMOffRamp1_2 = IAny2EVMOffRamp(0xdb92e73d1D630B5B7aC96840c4df0c591c7Ad23E); + vm.prank(alice); + l1.token.approve(address(l1.router), type(uint256).max); + deal(address(l1.token), alice, 1000e18); + deal(alice, 1000e18); + + vm.selectFork(l2.forkId); + l2.tokenPool = UpgradeableBurnMintTokenPool_ArbSepolia(0x3eC2b6F818B72442fc36561e9F930DD2b60957D2); + l2.router = IRouter(l2.tokenPool.getRouter()); + l1.chainSelector = l2.tokenPool.getSupportedChains()[0]; + l2.token = l2.tokenPool.getToken(); + l2.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(l2.router.getOnRamp(l1.chainSelector)); + l2.EVM2EVMOffRamp1_2 = IAny2EVMOffRamp(0xFf5e1c597c5DFfC896Ab8c7b9d876D513518c4b7); + vm.prank(alice); + l2.token.approve(address(l2.router), type(uint256).max); + deal(address(l2.token), alice, 1000e18); + deal(alice, 1000e18); + + vm.selectFork(l1.forkId); + assertEq(l1.chainSelector, 16015286601757825753); + assertEq(address(l1.token), 0xc4bF5CbDaBE595361438F8c6a187bDc330539c60); + assertEq(ITypeAndVersion(address(l1.router)).typeAndVersion(), "Router 1.2.0"); + assertEq(ITypeAndVersion(address(l1.EVM2EVMOnRamp1_2)).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(ITypeAndVersion(address(l1.EVM2EVMOffRamp1_2)).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); + assertTrue(l1.router.isOffRamp(l2.chainSelector, address(l1.EVM2EVMOffRamp1_2))); + // assert only one off ramp is set + IRouter.OffRamp[] memory offRamps = l1.router.getOffRamps(); + for (uint256 i; i < offRamps.length; ++i) { + if (offRamps[i].sourceChainSelector == l2.chainSelector) { + assertEq(address(l1.EVM2EVMOffRamp1_2), offRamps[i].offRamp); + } + } + + vm.selectFork(l2.forkId); + assertEq(l2.chainSelector, 3478487238524512106); + assertEq(address(l2.token), 0xb13Cfa6f8B2Eed2C37fB00fF0c1A59807C585810); + assertEq(ITypeAndVersion(address(l2.router)).typeAndVersion(), "Router 1.2.0"); + assertEq(ITypeAndVersion(address(l2.EVM2EVMOnRamp1_2)).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(ITypeAndVersion(address(l2.EVM2EVMOffRamp1_2)).typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); + assertTrue(l2.router.isOffRamp(l1.chainSelector, address(l2.EVM2EVMOffRamp1_2))); + // assert only one off ramp is set + offRamps = l2.router.getOffRamps(); + for (uint256 i; i < offRamps.length; ++i) { + if (offRamps[i].sourceChainSelector == l1.chainSelector) { + assertEq(address(l2.EVM2EVMOffRamp1_2), offRamps[i].offRamp); + } + } + } + + function testSendViaRouter() public { + uint256 amount = 10e18; + + Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); + message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); + + { + vm.selectFork(l1.forkId); + + uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + + vm.prank(alice); + l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); + } + { + vm.selectFork(l2.forkId); + + message.tokenAmounts[0].token = address(l2.token); + uint256 feeTokenAmount = l2.router.getFee(l1.chainSelector, message); + + vm.prank(alice); + l2.router.ccipSend{value: feeTokenAmount}(l1.chainSelector, message); + } + } + + function testLockOrBurnVia1_2OnRamp() public { + uint256 amount = 10e18; + { + vm.selectFork(l1.forkId); + + vm.prank(address(l1.EVM2EVMOnRamp1_2)); + l1.tokenPool.lockOrBurn(alice, abi.encode(alice), amount, l2.chainSelector, ""); + } + { + vm.selectFork(l2.forkId); + + // router is responsible for transferring liquidity, so we mock router.token.transferFrom(user, tokenPool) + deal(address(l2.token), address(l2.tokenPool), amount); + + vm.prank(address(l2.EVM2EVMOnRamp1_2)); + l2.tokenPool.lockOrBurn(alice, abi.encode(alice), amount, l1.chainSelector, ""); + } + } + + function testReleaseOrMintVia1_2OffRamp() public { + uint256 amount = 10e18; + { + vm.selectFork(l1.forkId); + + uint256 balanceBefore = l1.token.balanceOf(alice); + + vm.prank(address(l1.EVM2EVMOffRamp1_2)); + l1.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l2.chainSelector, ""); + + assertEq(l1.token.balanceOf(alice), balanceBefore + amount); + } + { + vm.selectFork(l2.forkId); + + uint256 balanceBefore = l2.token.balanceOf(alice); + + vm.prank(address(l2.EVM2EVMOffRamp1_2)); + l2.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l1.chainSelector, ""); + + assertEq(l2.token.balanceOf(alice), balanceBefore + amount); + } + } +} From 017391c697cc2ffb6609916c6dda8f98f62eff1c Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Sat, 19 Oct 2024 23:06:56 +0530 Subject: [PATCH 48/53] test: expect events fork before migration --- .../ForkBase.t.sol | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index d1240b613c..94fd1d0e14 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -257,6 +257,11 @@ contract ForkPoolAfterMigration is ForkBase { } contract ForkPoolBeforeMigration is ForkBase { + event Locked(address indexed sender, uint256 amount); + event Burned(address indexed sender, uint256 amount); + event Released(address indexed sender, address indexed recipient, uint256 amount); + event Minted(address indexed sender, address indexed recipient, uint256 amount); + uint256 internal constant BLOCK_BEFORE_MIGRATION_L1 = 6673089; uint256 internal constant BLOCK_BEFORE_MIGRATION_L2 = 79570677; @@ -321,24 +326,25 @@ contract ForkPoolBeforeMigration is ForkBase { function testSendViaRouter() public { uint256 amount = 10e18; - Client.EVM2AnyMessage memory message = _generateMessage(alice, 1); message.tokenAmounts[0] = Client.EVMTokenAmount({token: address(l1.token), amount: amount}); { vm.selectFork(l1.forkId); - uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); + vm.expectEmit(); + emit Locked(address(l1.EVM2EVMOnRamp1_2), amount); vm.prank(alice); l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); } { vm.selectFork(l2.forkId); - message.tokenAmounts[0].token = address(l2.token); uint256 feeTokenAmount = l2.router.getFee(l1.chainSelector, message); + vm.expectEmit(); + emit Burned(address(l2.EVM2EVMOnRamp1_2), amount); vm.prank(alice); l2.router.ccipSend{value: feeTokenAmount}(l1.chainSelector, message); } @@ -349,15 +355,18 @@ contract ForkPoolBeforeMigration is ForkBase { { vm.selectFork(l1.forkId); + vm.expectEmit(); + emit Locked(address(l1.EVM2EVMOnRamp1_2), amount); vm.prank(address(l1.EVM2EVMOnRamp1_2)); l1.tokenPool.lockOrBurn(alice, abi.encode(alice), amount, l2.chainSelector, ""); } { vm.selectFork(l2.forkId); - // router is responsible for transferring liquidity, so we mock router.token.transferFrom(user, tokenPool) deal(address(l2.token), address(l2.tokenPool), amount); + vm.expectEmit(); + emit Burned(address(l2.EVM2EVMOnRamp1_2), amount); vm.prank(address(l2.EVM2EVMOnRamp1_2)); l2.tokenPool.lockOrBurn(alice, abi.encode(alice), amount, l1.chainSelector, ""); } @@ -367,9 +376,10 @@ contract ForkPoolBeforeMigration is ForkBase { uint256 amount = 10e18; { vm.selectFork(l1.forkId); - uint256 balanceBefore = l1.token.balanceOf(alice); + vm.expectEmit(); + emit Released(address(l1.EVM2EVMOffRamp1_2), alice, amount); vm.prank(address(l1.EVM2EVMOffRamp1_2)); l1.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l2.chainSelector, ""); @@ -377,9 +387,10 @@ contract ForkPoolBeforeMigration is ForkBase { } { vm.selectFork(l2.forkId); - uint256 balanceBefore = l2.token.balanceOf(alice); + vm.expectEmit(); + emit Minted(address(l2.EVM2EVMOffRamp1_2), alice, amount); vm.prank(address(l2.EVM2EVMOffRamp1_2)); l2.tokenPool.releaseOrMint(abi.encode(alice), alice, amount, l1.chainSelector, ""); From b56ab36e0cbd958ec3c62eab4123d1bc5cc62854 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:48:10 +0530 Subject: [PATCH 49/53] chore: revert name change in test --- .../src/v0.8/ccip/test/pools/GHO/GhoBaseTest.t.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 22f23697cc..66d6fc63b5 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 @@ -51,7 +51,7 @@ abstract contract GhoBaseTest is BaseTest { emptyArray, router ); - TransparentUpgradeableProxy tokenProxyPool = new TransparentUpgradeableProxy( + TransparentUpgradeableProxy tokenPoolProxy = new TransparentUpgradeableProxy( address(tokenPoolImpl), proxyAdmin, tokenPoolInitParams @@ -59,10 +59,10 @@ abstract contract GhoBaseTest is BaseTest { // Manage ownership vm.stopPrank(); vm.prank(owner); - UpgradeableBurnMintTokenPool(address(tokenProxyPool)).acceptOwnership(); + UpgradeableBurnMintTokenPool(address(tokenPoolProxy)).acceptOwnership(); vm.startPrank(OWNER); - return address(tokenProxyPool); + return address(tokenPoolProxy); } function _deployUpgradeableLockReleaseTokenPool( @@ -83,7 +83,7 @@ abstract contract GhoBaseTest is BaseTest { router, bridgeLimit ); - TransparentUpgradeableProxy tokenProxyPool = new TransparentUpgradeableProxy( + TransparentUpgradeableProxy tokenPoolProxy = new TransparentUpgradeableProxy( address(tokenPoolImpl), proxyAdmin, tokenPoolInitParams @@ -92,10 +92,10 @@ abstract contract GhoBaseTest is BaseTest { // Manage ownership vm.stopPrank(); vm.prank(owner); - UpgradeableLockReleaseTokenPool(address(tokenProxyPool)).acceptOwnership(); + UpgradeableLockReleaseTokenPool(address(tokenPoolProxy)).acceptOwnership(); vm.startPrank(OWNER); - return address(tokenProxyPool); + return address(tokenPoolProxy); } function _inflateFacilitatorLevel(address tokenPool, address ghoToken, uint256 amount) internal { From 9dceff0ec9259cb3c0eafb978168ceb1b89adbcf Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:29:39 +0530 Subject: [PATCH 50/53] chore: upd comment --- contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 2 +- .../v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 4808aa3998..aa86725ef6 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -330,7 +330,7 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { } /// @notice Getter for proxy pool address. - /// @return proxyPool The proxy pool address for the given remoteChainSelector + /// @return proxyPool The proxy pool address. function getProxyPool() public view returns (address proxyPool) { assembly ("memory-safe") { proxyPool := shr(96, shl(96, sload(PROXY_POOL_SLOT))) diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index fa1012051c..07f48b2f14 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..4808aa3998 100644 +index b3571bb449..aa86725ef6 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,21 +1,24 @@ @@ -104,7 +104,7 @@ index b3571bb449..4808aa3998 100644 } + + /// @notice Getter for proxy pool address. -+ /// @return proxyPool The proxy pool address for the given remoteChainSelector ++ /// @return proxyPool The proxy pool address. + function getProxyPool() public view returns (address proxyPool) { + assembly ("memory-safe") { + proxyPool := shr(96, shl(96, sload(PROXY_POOL_SLOT))) From 7b49a4077353763f5ecc1331077c57b6430723cc Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:27:33 +0530 Subject: [PATCH 51/53] fix: rm disableInitializer as its handled by Initializable --- .../GHO/UpgradeableBurnMintTokenPool.sol | 4 +--- .../GHO/UpgradeableLockReleaseTokenPool.sol | 1 - .../UpgradeableBurnMintTokenPool_diff.md | 8 +++---- .../UpgradeableLockReleaseTokenPool_diff.md | 23 ++++++++++--------- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 57a3226ef4..99f8a7b42f 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -36,9 +36,7 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken address token, address armProxy, bool allowlistEnabled - ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) { - _disableInitializers(); - } + ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} /// @dev Initializer /// @dev The address passed as `owner` must accept ownership after initialization. diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index 7de31fa91e..a055c8b8cd 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -71,7 +71,6 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, bool acceptLiquidity ) UpgradeableTokenPool(IERC20(token), armProxy, allowlistEnabled) { i_acceptLiquidity = acceptLiquidity; - _disableInitializers(); } /// @dev Initializer diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index d7f14cdf40..ef2b054352 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -1,9 +1,9 @@ ```diff diff --git a/src/v0.8/ccip/pools/BurnMintTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -index 9af0f22f4c..57a3226ef4 100644 +index 9af0f22f4c..99f8a7b42f 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol -@@ -1,28 +1,92 @@ +@@ -1,28 +1,90 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; @@ -57,9 +57,7 @@ index 9af0f22f4c..57a3226ef4 100644 - address router - ) TokenPool(token, allowlist, armProxy, router) {} + bool allowlistEnabled -+ ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) { -+ _disableInitializers(); -+ } ++ ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} + + /// @dev Initializer + /// @dev The address passed as `owner` must accept ownership after initialization. diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md index d13b246408..47d9593e9f 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol -index 1a17fa0398..7de31fa91e 100644 +index 1a17fa0398..a055c8b8cd 100644 --- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -1,26 +1,39 @@ @@ -55,7 +55,7 @@ index 1a17fa0398..7de31fa91e 100644 string public constant override typeAndVersion = "LockReleaseTokenPool 1.4.0"; /// @dev The unique lock release pool flag to signal through EIP 165. -@@ -37,14 +50,53 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -37,16 +50,54 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @dev Can be address(0) if none is configured. address internal s_rateLimitAdmin; @@ -85,9 +85,8 @@ index 1a17fa0398..7de31fa91e 100644 + bool acceptLiquidity + ) UpgradeableTokenPool(IERC20(token), armProxy, allowlistEnabled) { i_acceptLiquidity = acceptLiquidity; -+ _disableInitializers(); -+ } -+ + } + + /// @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 @@ -111,10 +110,12 @@ index 1a17fa0398..7de31fa91e 100644 + _applyAllowListUpdates(new address[](0), allowlist); + } + s_bridgeLimit = bridgeLimit; - } - ++ } ++ /// @notice Locks the token in the pool -@@ -66,6 +118,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion + /// @param amount Amount to lock + /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised +@@ -66,6 +117,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion whenHealthy returns (bytes memory) { @@ -124,7 +125,7 @@ index 1a17fa0398..7de31fa91e 100644 _consumeOutboundRateLimit(remoteChainSelector, amount); emit Locked(msg.sender, amount); return ""; -@@ -83,6 +138,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -83,6 +137,11 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion uint64 remoteChainSelector, bytes memory ) external virtual override onlyOffRamp(remoteChainSelector) whenHealthy { @@ -136,7 +137,7 @@ index 1a17fa0398..7de31fa91e 100644 _consumeInboundRateLimit(remoteChainSelector, amount); getToken().safeTransfer(receiver, amount); emit Released(msg.sender, receiver, amount); -@@ -120,11 +180,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -120,11 +179,48 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion s_rateLimitAdmin = rateLimitAdmin; } @@ -185,7 +186,7 @@ index 1a17fa0398..7de31fa91e 100644 /// @notice Checks if the pool can accept liquidity. /// @return true if the pool can accept liquidity, false otherwise. function canAcceptLiquidity() external view returns (bool) { -@@ -151,7 +248,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -151,7 +247,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion emit LiquidityRemoved(msg.sender, amount); } From 7ae1477ef977af6b8cf493a7d36bf7857f79bd1a Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:28:09 +0530 Subject: [PATCH 52/53] chore: fix diff using diff algorithm patience --- ...gradeableBurnMintTokenPoolAbstract_diff.md | 6 ++-- .../UpgradeableBurnMintTokenPool_diff.md | 6 ++-- .../GHO/diffs/UpgradeableTokenPool_diff.md | 30 ++++++++----------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPoolAbstract_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPoolAbstract_diff.md index 2255b2ca44..a1aa3e58c9 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPoolAbstract_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPoolAbstract_diff.md @@ -7,13 +7,13 @@ index f5eb135186..e228732855 100644 // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; +pragma solidity ^0.8.0; - + -import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; +import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; - + -import {TokenPool} from "./TokenPool.sol"; +import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol"; - + -abstract contract BurnMintTokenPoolAbstract is TokenPool { +abstract contract UpgradeableBurnMintTokenPoolAbstract is UpgradeableTokenPool { /// @notice Contains the specific burn call for a pool. diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md index ef2b054352..ec7053ae04 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableBurnMintTokenPool_diff.md @@ -58,7 +58,8 @@ index 9af0f22f4c..99f8a7b42f 100644 - ) TokenPool(token, allowlist, armProxy, router) {} + bool allowlistEnabled + ) UpgradeableTokenPool(IBurnMintERC20(token), armProxy, allowlistEnabled) {} -+ + +- /// @inheritdoc BurnMintTokenPoolAbstract + /// @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 @@ -104,8 +105,7 @@ index 9af0f22f4c..99f8a7b42f 100644 + + _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); + } - -- /// @inheritdoc BurnMintTokenPoolAbstract ++ + /// @inheritdoc UpgradeableBurnMintTokenPoolAbstract function _burn(uint256 amount) internal virtual override { IBurnMintERC20(address(i_token)).burn(amount); diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index 07f48b2f14..9b6aa6e0e1 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -6,35 +6,31 @@ index b3571bb449..aa86725ef6 100644 @@ -1,21 +1,24 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.19; -- ++pragma solidity ^0.8.0; + -import {IPool} from "../interfaces/pools/IPool.sol"; -import {IARM} from "../interfaces/IARM.sol"; -import {IRouter} from "../interfaces/IRouter.sol"; -- ++import {IPool} from "../../interfaces/pools/IPool.sol"; ++import {IARM} from "../../interfaces/IARM.sol"; ++import {IRouter} from "../../interfaces/IRouter.sol"; + -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; -- ++import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; ++import {RateLimiter} from "../../libraries/RateLimiter.sol"; + -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; -import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; -- ++import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; ++import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; ++import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; + -/// @notice Base abstract class with common functions for all token pools. -/// A token pool serves as isolated place for holding tokens and token specific logic -/// that may execute as tokens move across the bridge. -abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { -+pragma solidity ^0.8.0; -+ -+import {IPool} from "../../interfaces/pools/IPool.sol"; -+import {IARM} from "../../interfaces/IARM.sol"; -+import {IRouter} from "../../interfaces/IRouter.sol"; -+ -+import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -+import {RateLimiter} from "../../libraries/RateLimiter.sol"; -+ -+import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -+import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; -+import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; -+ +/// @title UpgradeableTokenPool +/// @author Aave Labs +/// @notice Upgradeable version of Chainlink's CCIP TokenPool From 47a535bb3b5829c6d015fcdf8ccdc1357520a2c1 Mon Sep 17 00:00:00 2001 From: DhairyaSethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:24:14 +0530 Subject: [PATCH 53/53] fix: add proxyPool whitelist to onlyOffRamp modifier --- .../v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol | 4 +++- .../pools/GHO/diffs/UpgradeableTokenPool_diff.md | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index aa86725ef6..7cae344a2e 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -269,7 +269,9 @@ abstract contract UpgradeableTokenPool is IPool, OwnerIsCreator, IERC165 { /// is a permissioned offRamp for the given chain on the Router. modifier onlyOffRamp(uint64 remoteChainSelector) { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!s_router.isOffRamp(remoteChainSelector, msg.sender)) revert CallerIsNotARampOnRouter(msg.sender); + if (!(msg.sender == getProxyPool() || s_router.isOffRamp(remoteChainSelector, msg.sender))) { + revert CallerIsNotARampOnRouter(msg.sender); + } _; } diff --git a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md index 9b6aa6e0e1..741af46c1d 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md +++ b/contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableTokenPool_diff.md @@ -1,6 +1,6 @@ ```diff diff --git a/src/v0.8/ccip/pools/TokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol -index b3571bb449..aa86725ef6 100644 +index b3571bb449..7cae344a2e 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,21 +1,24 @@ @@ -94,7 +94,18 @@ index b3571bb449..aa86725ef6 100644 _; } -@@ -323,4 +328,21 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { +@@ -264,7 +269,9 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { + /// is a permissioned offRamp for the given chain on the Router. + modifier onlyOffRamp(uint64 remoteChainSelector) { + if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); +- if (!s_router.isOffRamp(remoteChainSelector, msg.sender)) revert CallerIsNotARampOnRouter(msg.sender); ++ if (!(msg.sender == getProxyPool() || s_router.isOffRamp(remoteChainSelector, msg.sender))) { ++ revert CallerIsNotARampOnRouter(msg.sender); ++ } + _; + } + +@@ -323,4 +330,21 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 { if (IARM(i_armProxy).isCursed()) revert BadARMSignal(); _; }