diff --git a/src/test/TestGhoStewardsForkEthMainnet.t.sol b/src/test/TestGhoStewardsForkEthMainnet.t.sol deleted file mode 100644 index f525d325..00000000 --- a/src/test/TestGhoStewardsForkEthMainnet.t.sol +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import 'forge-std/Test.sol'; -import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol'; -import {IACLManager} from '@aave/core-v3/contracts/interfaces/IACLManager.sol'; -import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; -import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; -import {IPoolAddressesProvider, IPoolDataProvider, IPool} from 'aave-address-book/AaveV3.sol'; -import {DataTypes} from 'aave-v3-core/contracts/protocol/libraries/types/DataTypes.sol'; -import {ReserveConfiguration} from 'aave-v3-core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol'; -import {FixedFeeStrategyFactory} from '../contracts/facilitators/gsm/feeStrategy/FixedFeeStrategyFactory.sol'; -import {IGsmFeeStrategy} from '../contracts/facilitators/gsm/feeStrategy/interfaces/IGsmFeeStrategy.sol'; -import {Gsm} from '../contracts/facilitators/gsm/Gsm.sol'; -import {GhoToken} from '../contracts/gho/GhoToken.sol'; -import {IGhoAaveSteward} from '../contracts/misc/interfaces/IGhoAaveSteward.sol'; -import {GhoAaveSteward} from '../contracts/misc/GhoAaveSteward.sol'; -import {GhoBucketSteward} from '../contracts/misc/GhoBucketSteward.sol'; -import {GhoCcipSteward} from '../contracts/misc/GhoCcipSteward.sol'; -import {GhoGsmSteward} from '../contracts/misc/GhoGsmSteward.sol'; -import {RateLimiter, IUpgradeableLockReleaseTokenPool} from '../contracts/misc/dependencies/Ccip.sol'; -import {IDefaultInterestRateStrategyV2} from '../contracts/misc/dependencies/AaveV3-1.sol'; -import {MockPool} from './mocks/MockPool.sol'; -import {MockUpgradeableLockReleaseTokenPool} from './mocks/MockUpgradeableLockReleaseTokenPool.sol'; - -contract TestGhoStewardsForkEthMainnet is Test { - using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - - address public OWNER = makeAddr('OWNER'); - address public RISK_COUNCIL = makeAddr('RISK_COUNCIL'); - IPoolDataProvider public POOL_DATA_PROVIDER = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER; - IPoolAddressesProvider public POOL_ADDRESSES_PROVIDER = AaveV3Ethereum.POOL_ADDRESSES_PROVIDER; - address public GHO_TOKEN = AaveV3EthereumAssets.GHO_UNDERLYING; - address public GHO_ATOKEN = AaveV3EthereumAssets.GHO_A_TOKEN; - IPool public POOL = AaveV3Ethereum.POOL; - address public ACL_ADMIN = AaveV3Ethereum.ACL_ADMIN; - address public GHO_TOKEN_POOL = MiscEthereum.GHO_CCIP_TOKEN_POOL; - address public GHO_GSM_USDC = MiscEthereum.GSM_USDC; - address public GHO_GSM_USDT = MiscEthereum.GSM_USDT; - address public ACL_MANAGER; - - GhoAaveSteward public GHO_AAVE_STEWARD; - GhoBucketSteward public GHO_BUCKET_STEWARD; - GhoCcipSteward public GHO_CCIP_STEWARD; - GhoGsmSteward public GHO_GSM_STEWARD; - - uint64 public remoteChainSelector = 4949039107694359620; - - event ChainConfigured( - uint64 remoteChainSelector, - RateLimiter.Config outboundRateLimiterConfig, - RateLimiter.Config inboundRateLimiterConfig - ); - - function setUp() public { - vm.createSelectFork(vm.rpcUrl('mainnet'), 20580302); - vm.startPrank(ACL_ADMIN); - ACL_MANAGER = POOL_ADDRESSES_PROVIDER.getACLManager(); - - IGhoAaveSteward.BorrowRateConfig memory defaultBorrowRateConfig = IGhoAaveSteward - .BorrowRateConfig({ - optimalUsageRatioMaxChange: 10_00, - baseVariableBorrowRateMaxChange: 5_00, - variableRateSlope1MaxChange: 10_00, - variableRateSlope2MaxChange: 10_00 - }); - - GHO_AAVE_STEWARD = new GhoAaveSteward( - OWNER, - address(POOL_ADDRESSES_PROVIDER), - address(POOL_DATA_PROVIDER), - GHO_TOKEN, - RISK_COUNCIL, - defaultBorrowRateConfig - ); - IAccessControl(ACL_MANAGER).grantRole( - IACLManager(ACL_MANAGER).RISK_ADMIN_ROLE(), - address(GHO_AAVE_STEWARD) - ); - - GHO_BUCKET_STEWARD = new GhoBucketSteward(OWNER, GHO_TOKEN, RISK_COUNCIL); - GhoToken(GHO_TOKEN).grantRole( - GhoToken(GHO_TOKEN).BUCKET_MANAGER_ROLE(), - address(GHO_BUCKET_STEWARD) - ); - - GHO_CCIP_STEWARD = new GhoCcipSteward(GHO_TOKEN, GHO_TOKEN_POOL, RISK_COUNCIL, true); - IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).setRateLimitAdmin(address(GHO_CCIP_STEWARD)); - IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).setBridgeLimitAdmin(address(GHO_CCIP_STEWARD)); - - FixedFeeStrategyFactory strategyFactory = new FixedFeeStrategyFactory(); - GHO_GSM_STEWARD = new GhoGsmSteward(address(strategyFactory), RISK_COUNCIL); - Gsm(GHO_GSM_USDC).grantRole(Gsm(GHO_GSM_USDC).CONFIGURATOR_ROLE(), address(GHO_GSM_STEWARD)); - Gsm(GHO_GSM_USDT).grantRole(Gsm(GHO_GSM_USDT).CONFIGURATOR_ROLE(), address(GHO_GSM_STEWARD)); - - address[] memory controlledFacilitators = new address[](3); - controlledFacilitators[0] = address(GHO_ATOKEN); - controlledFacilitators[1] = address(GHO_GSM_USDC); - controlledFacilitators[2] = address(GHO_GSM_USDT); - changePrank(OWNER); - GHO_BUCKET_STEWARD.setControlledFacilitator(controlledFacilitators, true); - - vm.stopPrank(); - } - - function testStewardsPermissions() public { - assertEq( - IAccessControl(ACL_MANAGER).hasRole( - IACLManager(ACL_MANAGER).RISK_ADMIN_ROLE(), - address(GHO_AAVE_STEWARD) - ), - true - ); - - assertEq( - IAccessControl(GHO_TOKEN).hasRole( - GhoToken(GHO_TOKEN).BUCKET_MANAGER_ROLE(), - address(GHO_BUCKET_STEWARD) - ), - true - ); - - assertEq( - IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).getRateLimitAdmin(), - address(GHO_CCIP_STEWARD) - ); - assertEq( - IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).getBridgeLimitAdmin(), - address(GHO_CCIP_STEWARD) - ); - - assertEq( - Gsm(GHO_GSM_USDC).hasRole(Gsm(GHO_GSM_USDC).CONFIGURATOR_ROLE(), address(GHO_GSM_STEWARD)), - true - ); - assertEq( - Gsm(GHO_GSM_USDT).hasRole(Gsm(GHO_GSM_USDT).CONFIGURATOR_ROLE(), address(GHO_GSM_STEWARD)), - true - ); - } - - function testGhoAaveStewardUpdateGhoBorrowCap() public { - uint256 currentBorrowCap = _getGhoBorrowCap(); - uint256 newBorrowCap = currentBorrowCap + 1; - vm.prank(RISK_COUNCIL); - GHO_AAVE_STEWARD.updateGhoBorrowCap(newBorrowCap); - assertEq(_getGhoBorrowCap(), newBorrowCap); - } - - function testGhoAaveStewardUpdateGhoSupplyCap() public { - uint256 currentSupplyCap = _getGhoSupplyCap(); - assertEq(currentSupplyCap, 0); - uint256 newSupplyCap = currentSupplyCap + 1; - // Can't update supply cap even by 1 since it's 0, and 100% of 0 is 0 - vm.expectRevert('INVALID_SUPPLY_CAP_UPDATE'); - vm.prank(RISK_COUNCIL); - GHO_AAVE_STEWARD.updateGhoSupplyCap(newSupplyCap); - } - - function testGhoAaveStewardUpdateGhoBorrowRate() public { - IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates(); - vm.prank(RISK_COUNCIL); - GHO_AAVE_STEWARD.updateGhoBorrowRate( - currentRates.optimalUsageRatio - 1, - currentRates.baseVariableBorrowRate + 1, - currentRates.variableRateSlope1 + 1, - currentRates.variableRateSlope2 + 1 - ); - assertEq(_getOptimalUsageRatio(), currentRates.optimalUsageRatio - 1); - assertEq(_getBaseVariableBorrowRate(), currentRates.baseVariableBorrowRate + 1); - assertEq(_getVariableRateSlope1(), currentRates.variableRateSlope1 + 1); - assertEq(_getVariableRateSlope2(), currentRates.variableRateSlope2 + 1); - } - - function testGhoBucketStewardUpdateFacilitatorBucketCapacity() public { - (uint256 currentBucketCapacity, ) = GhoToken(GHO_TOKEN).getFacilitatorBucket( - address(GHO_ATOKEN) - ); - vm.prank(RISK_COUNCIL); - uint128 newBucketCapacity = uint128(currentBucketCapacity) + 1; - GHO_BUCKET_STEWARD.updateFacilitatorBucketCapacity(address(GHO_ATOKEN), newBucketCapacity); - (uint256 capacity, ) = GhoToken(GHO_TOKEN).getFacilitatorBucket(address(GHO_ATOKEN)); - assertEq(newBucketCapacity, capacity); - } - - function testGhoBucketStewardSetControlledFacilitator() public { - address[] memory newGsmList = new address[](1); - address gho_gsm_4626 = makeAddr('gho_gsm_4626'); - newGsmList[0] = gho_gsm_4626; - vm.prank(OWNER); - GHO_BUCKET_STEWARD.setControlledFacilitator(newGsmList, true); - assertTrue(_isControlledFacilitator(gho_gsm_4626)); - vm.prank(OWNER); - GHO_BUCKET_STEWARD.setControlledFacilitator(newGsmList, false); - assertFalse(_isControlledFacilitator(gho_gsm_4626)); - } - - function testGhoCcipStewardUpdateBridgeLimit() public { - uint256 oldBridgeLimit = IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).getBridgeLimit(); - uint256 newBridgeLimit = oldBridgeLimit + 1; - vm.prank(RISK_COUNCIL); - GHO_CCIP_STEWARD.updateBridgeLimit(newBridgeLimit); - uint256 currentBridgeLimit = IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).getBridgeLimit(); - assertEq(currentBridgeLimit, newBridgeLimit); - } - - function testGhoCcipStewardUpdateRateLimit() public { - RateLimiter.TokenBucket memory outboundConfig = MockUpgradeableLockReleaseTokenPool( - GHO_TOKEN_POOL - ).getCurrentOutboundRateLimiterState(remoteChainSelector); - RateLimiter.TokenBucket memory inboundConfig = MockUpgradeableLockReleaseTokenPool( - GHO_TOKEN_POOL - ).getCurrentInboundRateLimiterState(remoteChainSelector); - - RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({ - isEnabled: outboundConfig.isEnabled, - capacity: outboundConfig.capacity + 1, - rate: outboundConfig.rate - }); - - RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ - isEnabled: outboundConfig.isEnabled, - capacity: inboundConfig.capacity, - rate: inboundConfig.rate - }); - - // Currently rate limit set to 0, so can't even change by 1 because 100% of 0 is 0 - vm.expectRevert('INVALID_RATE_LIMIT_UPDATE'); - vm.prank(RISK_COUNCIL); - GHO_CCIP_STEWARD.updateRateLimit( - remoteChainSelector, - newOutboundConfig.isEnabled, - newOutboundConfig.capacity, - newOutboundConfig.rate, - newInboundConfig.isEnabled, - newInboundConfig.capacity, - newInboundConfig.rate - ); - } - - function testGhoGsmStewardUpdateExposureCap() public { - uint128 oldExposureCap = Gsm(GHO_GSM_USDC).getExposureCap(); - uint128 newExposureCap = oldExposureCap + 1; - vm.prank(RISK_COUNCIL); - GHO_GSM_STEWARD.updateGsmExposureCap(GHO_GSM_USDC, newExposureCap); - uint128 currentExposureCap = Gsm(GHO_GSM_USDC).getExposureCap(); - assertEq(currentExposureCap, newExposureCap); - } - - function testGhoGsmStewardUpdateGsmBuySellFees() public { - address feeStrategy = Gsm(GHO_GSM_USDC).getFeeStrategy(); - uint256 buyFee = IGsmFeeStrategy(feeStrategy).getBuyFee(1e4); - uint256 sellFee = IGsmFeeStrategy(feeStrategy).getSellFee(1e4); - vm.prank(RISK_COUNCIL); - GHO_GSM_STEWARD.updateGsmBuySellFees(GHO_GSM_USDC, buyFee + 1, sellFee); - address newStrategy = Gsm(GHO_GSM_USDC).getFeeStrategy(); - uint256 newBuyFee = IGsmFeeStrategy(newStrategy).getBuyFee(1e4); - assertEq(newBuyFee, buyFee + 1); - } - - function _getGhoBorrowCap() internal view returns (uint256) { - DataTypes.ReserveConfigurationMap memory configuration = POOL.getConfiguration(GHO_TOKEN); - return configuration.getBorrowCap(); - } - - function _getGhoSupplyCap() internal view returns (uint256) { - DataTypes.ReserveConfigurationMap memory configuration = POOL.getConfiguration( - address(GHO_TOKEN) - ); - return configuration.getSupplyCap(); - } - - function _getOptimalUsageRatio() internal view returns (uint16) { - IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates(); - return currentRates.optimalUsageRatio; - } - - function _getBaseVariableBorrowRate() internal view returns (uint32) { - IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates(); - return currentRates.baseVariableBorrowRate; - } - - function _getVariableRateSlope1() internal view returns (uint32) { - IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates(); - return currentRates.variableRateSlope1; - } - - function _getVariableRateSlope2() internal view returns (uint32) { - IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates(); - return currentRates.variableRateSlope2; - } - - function _getGhoBorrowRates() - internal - view - returns (IDefaultInterestRateStrategyV2.InterestRateData memory) - { - address rateStrategyAddress = POOL_DATA_PROVIDER.getInterestRateStrategyAddress(GHO_TOKEN); - return IDefaultInterestRateStrategyV2(rateStrategyAddress).getInterestRateDataBps(GHO_TOKEN); - } - - function _isControlledFacilitator(address target) internal view returns (bool) { - address[] memory controlledFacilitators = GHO_BUCKET_STEWARD.getControlledFacilitators(); - for (uint256 i = 0; i < controlledFacilitators.length; i++) { - if (controlledFacilitators[i] == target) { - return true; - } - } - return false; - } -} diff --git a/src/test/TestGhoStewardsForkRemote.t.sol b/src/test/TestGhoStewardsForkRemote.t.sol index 38cf4fa8..a7adfbfe 100644 --- a/src/test/TestGhoStewardsForkRemote.t.sol +++ b/src/test/TestGhoStewardsForkRemote.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol'; +import {TransparentUpgradeableProxy} from 'solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol'; import {AaveV3Arbitrum, AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; import {MiscArbitrum} from 'aave-address-book/MiscArbitrum.sol'; import {IPoolAddressesProvider, IPoolDataProvider, IPool} from 'aave-address-book/AaveV3.sol'; @@ -12,6 +13,7 @@ import {GhoCcipSteward} from '../contracts/misc/GhoCcipSteward.sol'; import {RateLimiter, IUpgradeableLockReleaseTokenPool} from '../contracts/misc/dependencies/Ccip.sol'; import {IDefaultInterestRateStrategyV2} from '../contracts/misc/dependencies/AaveV3-1.sol'; import {MockUpgradeableLockReleaseTokenPool} from './mocks/MockUpgradeableLockReleaseTokenPool.sol'; +import {MockUpgradeableBurnMintTokenPool} from './mocks/MockUpgradeableBurnMintTokenPool.sol'; contract TestGhoStewardsForkRemote is Test { address public OWNER = makeAddr('OWNER'); @@ -20,9 +22,11 @@ contract TestGhoStewardsForkRemote is Test { IPoolAddressesProvider public POOL_ADDRESSES_PROVIDER = AaveV3Arbitrum.POOL_ADDRESSES_PROVIDER; address public GHO_TOKEN = 0x7dfF72693f6A4149b17e7C6314655f6A9F7c8B33; address public GHO_ATOKEN = 0xeBe517846d0F36eCEd99C735cbF6131e1fEB775D; + address public ARM_PROXY = 0xC311a21e6fEf769344EB1515588B9d535662a145; IPool public POOL = AaveV3Arbitrum.POOL; address public ACL_ADMIN = AaveV3Arbitrum.ACL_ADMIN; address public GHO_TOKEN_POOL = MiscArbitrum.GHO_CCIP_TOKEN_POOL; + address public PROXY_ADMIN = MiscArbitrum.PROXY_ADMIN; address public ACL_MANAGER; GhoBucketSteward public GHO_BUCKET_STEWARD; @@ -48,11 +52,8 @@ contract TestGhoStewardsForkRemote is Test { ); GHO_CCIP_STEWARD = new GhoCcipSteward(GHO_TOKEN, GHO_TOKEN_POOL, RISK_COUNCIL, true); - - /// Currently cannot set rate limit admin on remote pool - //IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).setRateLimitAdmin(address(GHO_CCIP_STEWARD)); - address[] memory controlledFacilitators = new address[](3); + address[] memory controlledFacilitators = new address[](1); controlledFacilitators[0] = address(GHO_ATOKEN); changePrank(OWNER); GHO_BUCKET_STEWARD.setControlledFacilitator(controlledFacilitators, true); @@ -68,13 +69,6 @@ contract TestGhoStewardsForkRemote is Test { ), true ); - /// Currently there is no getRateLimitAdmin() func on the remote pool - /* - assertEq( - IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).getRateLimitAdmin(), - address(GHO_CCIP_STEWARD) - ); - */ } function testGhoBucketStewardUpdateFacilitatorBucketCapacity() public { @@ -136,6 +130,129 @@ contract TestGhoStewardsForkRemote is Test { ); } + function testGhoCcipStewardRevertUpdateRateLimitUnauthorizedBeforeUpgrade() public { + RateLimiter.TokenBucket memory mockConfig = RateLimiter.TokenBucket({ + rate: 50, + capacity: 50, + tokens: 1, + lastUpdated: 1, + isEnabled: true + }); + // Mocking response due to rate limit currently being 0 + vm.mockCall( + GHO_TOKEN_POOL, + abi.encodeWithSelector( + IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL) + .getCurrentOutboundRateLimiterState + .selector, + remoteChainSelector + ), + abi.encode(mockConfig) + ); + + RateLimiter.TokenBucket memory outboundConfig = IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL) + .getCurrentOutboundRateLimiterState(remoteChainSelector); + RateLimiter.TokenBucket memory inboundConfig = IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL) + .getCurrentInboundRateLimiterState(remoteChainSelector); + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: outboundConfig.capacity, + rate: outboundConfig.rate + 1 + }); + + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: inboundConfig.capacity, + rate: inboundConfig.rate + }); + + vm.expectRevert('Only callable by owner'); + vm.prank(RISK_COUNCIL); + GHO_CCIP_STEWARD.updateRateLimit( + remoteChainSelector, + newOutboundConfig.isEnabled, + newOutboundConfig.capacity, + newOutboundConfig.rate, + newInboundConfig.isEnabled, + newInboundConfig.capacity, + newInboundConfig.rate + ); + } + + function testGhoCcipStewardUpdateRateLimitAfterPoolUpgrade() public { + MockUpgradeableBurnMintTokenPool tokenPoolImpl = new MockUpgradeableBurnMintTokenPool( + address(GHO_TOKEN), + address(ARM_PROXY), + false, + false + ); + + vm.prank(PROXY_ADMIN); + TransparentUpgradeableProxy(payable(address(GHO_TOKEN_POOL))).upgradeTo(address(tokenPoolImpl)); + + vm.prank(ACL_ADMIN); + IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).setRateLimitAdmin(address(GHO_CCIP_STEWARD)); + + RateLimiter.TokenBucket memory mockConfig = RateLimiter.TokenBucket({ + rate: 50, + capacity: 50, + tokens: 1, + lastUpdated: 1, + isEnabled: true + }); + + // Mocking response due to rate limit currently being 0 + vm.mockCall( + GHO_TOKEN_POOL, + abi.encodeWithSelector( + IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL) + .getCurrentOutboundRateLimiterState + .selector, + remoteChainSelector + ), + abi.encode(mockConfig) + ); + vm.mockCall( + GHO_TOKEN_POOL, + abi.encodeWithSelector( + IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL).getCurrentInboundRateLimiterState.selector, + remoteChainSelector + ), + abi.encode(mockConfig) + ); + + RateLimiter.TokenBucket memory outboundConfig = IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL) + .getCurrentOutboundRateLimiterState(remoteChainSelector); + RateLimiter.TokenBucket memory inboundConfig = IUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL) + .getCurrentInboundRateLimiterState(remoteChainSelector); + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: outboundConfig.capacity + 1, + rate: outboundConfig.rate + }); + + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: inboundConfig.capacity + 1, + rate: inboundConfig.rate + }); + + vm.expectEmit(false, false, false, true); + emit ChainConfigured(remoteChainSelector, newOutboundConfig, newInboundConfig); + vm.prank(RISK_COUNCIL); + GHO_CCIP_STEWARD.updateRateLimit( + remoteChainSelector, + newOutboundConfig.isEnabled, + newOutboundConfig.capacity, + newOutboundConfig.rate, + newInboundConfig.isEnabled, + newInboundConfig.capacity, + newInboundConfig.rate + ); + } + function _getOptimalUsageRatio() internal view returns (uint16) { IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates(); return currentRates.optimalUsageRatio; diff --git a/src/test/mocks/MockUpgradeableBurnMintTokenPool.sol b/src/test/mocks/MockUpgradeableBurnMintTokenPool.sol new file mode 100644 index 00000000..45b78af8 --- /dev/null +++ b/src/test/mocks/MockUpgradeableBurnMintTokenPool.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; +import {Initializable} from 'solidity-utils/contracts/transparent-proxy/Initializable.sol'; +import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; +import {RateLimiter} from 'src/contracts/misc/dependencies/Ccip.sol'; +import {IRouter} from 'src/contracts/misc/dependencies/Ccip.sol'; +import {IARM} from 'src/contracts/misc/dependencies/AaveV3-1.sol'; + +contract MockUpgradeableBurnMintTokenPool is Initializable { + using SafeERC20 for IERC20; + using RateLimiter for RateLimiter.TokenBucket; + + error Unauthorized(address caller); + error ZeroAddressNotAllowed(); + + event ChainConfigured( + uint64 remoteChainSelector, + RateLimiter.Config outboundRateLimiterConfig, + RateLimiter.Config inboundRateLimiterConfig + ); + + struct ChainUpdate { + uint64 remoteChainSelector; + bool allowed; + RateLimiter.Config outboundRateLimiterConfig; + RateLimiter.Config inboundRateLimiterConfig; + } + + address internal _owner; + bool internal immutable i_acceptLiquidity; + address internal s_rateLimitAdmin; + uint256 private s_bridgeLimit; + address internal s_bridgeLimitAdmin; + IERC20 internal immutable i_token; + address internal immutable i_armProxy; + bool internal immutable i_allowlistEnabled; + EnumerableSet.AddressSet internal s_allowList; + IRouter internal s_router; + EnumerableSet.UintSet internal s_remoteChainSelectors; + mapping(uint64 => RateLimiter.TokenBucket) internal s_outboundRateLimits; + mapping(uint64 => RateLimiter.TokenBucket) internal s_inboundRateLimits; + + constructor(address token, address armProxy, bool allowlistEnabled, bool acceptLiquidity) { + i_acceptLiquidity = acceptLiquidity; + if (address(token) == address(0)) revert ZeroAddressNotAllowed(); + i_token = IERC20(token); + i_armProxy = armProxy; + i_allowlistEnabled = allowlistEnabled; + } + + 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); + s_bridgeLimit = bridgeLimit; + } + + function owner() public view returns (address) { + return _owner; + } + + function acceptOwnership() external {} + + function setRateLimitAdmin(address rateLimitAdmin) external { + s_rateLimitAdmin = rateLimitAdmin; + } + + function setBridgeLimit(uint256 newBridgeLimit) external { + if (msg.sender != s_bridgeLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + uint256 oldBridgeLimit = s_bridgeLimit; + s_bridgeLimit = newBridgeLimit; + } + + function setBridgeLimitAdmin(address bridgeLimitAdmin) external { + address oldAdmin = s_bridgeLimitAdmin; + s_bridgeLimitAdmin = bridgeLimitAdmin; + } + + function getBridgeLimit() external view virtual returns (uint256) { + return s_bridgeLimit; + } + + function getRateLimitAdmin() external view returns (address) { + return s_rateLimitAdmin; + } + + function getBridgeLimitAdmin() external view returns (address) { + return s_bridgeLimitAdmin; + } + + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + RateLimiter.Config memory outboundConfig, + RateLimiter.Config memory inboundConfig + ) external { + if (msg.sender != s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + + _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); + } + + function _setRateLimitConfig( + uint64 remoteChainSelector, + RateLimiter.Config memory outboundConfig, + RateLimiter.Config memory inboundConfig + ) internal { + RateLimiter._validateTokenBucketConfig(outboundConfig, false); + s_outboundRateLimits[remoteChainSelector]._setTokenBucketConfig(outboundConfig); + RateLimiter._validateTokenBucketConfig(inboundConfig, false); + s_inboundRateLimits[remoteChainSelector]._setTokenBucketConfig(inboundConfig); + emit ChainConfigured(remoteChainSelector, outboundConfig, inboundConfig); + } + + function getCurrentOutboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (RateLimiter.TokenBucket memory) { + return s_outboundRateLimits[remoteChainSelector]._currentTokenBucketState(); + } + + function getCurrentInboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (RateLimiter.TokenBucket memory) { + return s_inboundRateLimits[remoteChainSelector]._currentTokenBucketState(); + } + + function applyChainUpdates(ChainUpdate[] calldata chains) external virtual { + for (uint256 i = 0; i < chains.length; ++i) { + ChainUpdate memory update = chains[i]; + s_outboundRateLimits[update.remoteChainSelector] = RateLimiter.TokenBucket({ + rate: update.outboundRateLimiterConfig.rate, + capacity: update.outboundRateLimiterConfig.capacity, + tokens: update.outboundRateLimiterConfig.capacity, + lastUpdated: uint32(block.timestamp), + isEnabled: update.outboundRateLimiterConfig.isEnabled + }); + + s_inboundRateLimits[update.remoteChainSelector] = RateLimiter.TokenBucket({ + rate: update.inboundRateLimiterConfig.rate, + capacity: update.inboundRateLimiterConfig.capacity, + tokens: update.inboundRateLimiterConfig.capacity, + lastUpdated: uint32(block.timestamp), + isEnabled: update.inboundRateLimiterConfig.isEnabled + }); + } + } + + function _transferOwnership(address newOwner) internal { + _owner = newOwner; + } +}