diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol index 80ef621eff..5f1480d301 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -40,7 +40,7 @@ contract UpgradeableBurnMintTokenPool is Initializable, UpgradeableBurnMintToken if (router == address(0) || owner_ == address(0)) revert ZeroAddressNotAllowed(); _transferOwnership(owner_); - s_router = IRouter(router); + _getTokenPoolStorage().s_router = IRouter(router); if (i_allowlistEnabled) _applyAllowListUpdates(new address[](0), allowlist); } diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol index 5d5e055299..feb142b89e 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -50,17 +50,23 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, /// 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 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; + + /// @custom:storage-location erc7201:aave-ccip.storage.UpgradeableLockReleaseTokenPool + struct UpgradeableLockReleaseTokenPoolStorage { + /// @notice The address of the rebalancer. + address s_rebalancer; + /// @notice Maximum amount of tokens that can be bridged to other chains + uint256 s_bridgeLimit; + /// @notice Amount of tokens bridged (transferred out) + /// @dev Must always be equal to or below the bridge limit + uint256 s_currentBridged; + /// @notice The address of the bridge limit admin. + /// @dev Can be address(0) if none is configured. + address s_bridgeLimitAdmin; + } + + // bytes32 private constant lockReleasePoolStorage = keccak256(abi.encode(uint256(keccak256("aave-ccip.storage.UpgradeableLockReleaseTokenPool")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant lockReleasePoolStorage = 0x0f06852668cdd7d8554206875bc1ed67644f9d2a4b038c34789cc8b26cb42300; // @notice Constructor // @param token The bridgeable token that is managed by this pool. @@ -94,9 +100,9 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, if (router == address(0) || owner_ == address(0)) revert ZeroAddressNotAllowed(); _transferOwnership(owner_); - s_router = IRouter(router); + _getTokenPoolStorage().s_router = IRouter(router); if (i_allowlistEnabled) _applyAllowListUpdates(new address[](0), allowlist); - s_bridgeLimit = bridgeLimit; + _getLockReleasePoolStorage().s_bridgeLimit = bridgeLimit; } /// @notice Locks the token in the pool @@ -104,8 +110,10 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, function lockOrBurn( Pool.LockOrBurnInV1 calldata lockOrBurnIn ) external virtual override returns (Pool.LockOrBurnOutV1 memory) { + UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); + // Increase bridged amount because tokens are leaving the source chain - if ((s_currentBridged += lockOrBurnIn.amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit); + if (($.s_currentBridged += lockOrBurnIn.amount) > $.s_bridgeLimit) revert BridgeLimitExceeded($.s_bridgeLimit); _validateLockOrBurn(lockOrBurnIn); @@ -123,10 +131,12 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, function releaseOrMint( Pool.ReleaseOrMintInV1 calldata releaseOrMintIn ) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) { + UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); + // This should never occur. Amount should never exceed the current bridged amount - if (releaseOrMintIn.amount > s_currentBridged) revert NotEnoughBridgedAmount(); + if (releaseOrMintIn.amount > $.s_currentBridged) revert NotEnoughBridgedAmount(); // Reduce bridged amount because tokens are back to source chain - s_currentBridged -= releaseOrMintIn.amount; + $.s_currentBridged -= releaseOrMintIn.amount; _validateReleaseOrMint(releaseOrMintIn); @@ -152,13 +162,13 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, /// @notice Gets LiquidityManager, can be address(0) if none is configured. /// @return The current liquidity manager. function getRebalancer() external view returns (address) { - return s_rebalancer; + return _getLockReleasePoolStorage().s_rebalancer; } /// @notice Sets the LiquidityManager address. /// @dev Only callable by the owner. function setRebalancer(address rebalancer) external onlyOwner { - s_rebalancer = rebalancer; + _getLockReleasePoolStorage().s_rebalancer = rebalancer; } /// @notice Sets the current bridged amount to other chains @@ -166,7 +176,7 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, /// @dev Does not emit event, it is expected to only be called during token pool migrations. /// @param newCurrentBridged The new bridged amount function setCurrentBridgedAmount(uint256 newCurrentBridged) external onlyOwner { - s_currentBridged = newCurrentBridged; + _getLockReleasePoolStorage().s_currentBridged = newCurrentBridged; } /// @notice Sets the bridge limit, the maximum amount of tokens that can be bridged out @@ -174,9 +184,11 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, /// @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; + UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); + + if (msg.sender != $.s_bridgeLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + uint256 oldBridgeLimit = $.s_bridgeLimit; + $.s_bridgeLimit = newBridgeLimit; emit BridgeLimitUpdated(oldBridgeLimit, newBridgeLimit); } @@ -184,26 +196,28 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, /// @dev Only callable by the owner. /// @param bridgeLimitAdmin The new bridge limit admin address. function setBridgeLimitAdmin(address bridgeLimitAdmin) external onlyOwner { - address oldAdmin = s_bridgeLimitAdmin; - s_bridgeLimitAdmin = bridgeLimitAdmin; + UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); + + address oldAdmin = $.s_bridgeLimitAdmin; + $.s_bridgeLimitAdmin = bridgeLimitAdmin; emit BridgeLimitAdminUpdated(oldAdmin, 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; + return _getLockReleasePoolStorage().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; + return _getLockReleasePoolStorage().s_currentBridged; } /// @notice Gets the bridge limiter admin address. function getBridgeLimitAdmin() external view returns (address) { - return s_bridgeLimitAdmin; + return _getLockReleasePoolStorage().s_bridgeLimitAdmin; } /// @notice Checks if the pool can accept liquidity. @@ -216,7 +230,7 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, /// @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); + if (_getLockReleasePoolStorage().s_rebalancer != msg.sender) revert Unauthorized(msg.sender); i_token.safeTransferFrom(msg.sender, address(this), amount); emit LiquidityAdded(msg.sender, amount); @@ -225,7 +239,7 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, /// @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 (_getLockReleasePoolStorage().s_rebalancer != msg.sender) revert Unauthorized(msg.sender); if (i_token.balanceOf(address(this)) < amount) revert InsufficientLiquidity(); i_token.safeTransfer(msg.sender, amount); @@ -248,4 +262,10 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool, emit LiquidityTransferred(from, amount); } + + function _getLockReleasePoolStorage() internal pure returns (UpgradeableLockReleaseTokenPoolStorage storage $) { + assembly ("memory-safe") { + $.slot := lockReleasePoolStorage + } + } } diff --git a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol index 8c0965a67f..d0eca208e3 100644 --- a/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -102,23 +102,30 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { address internal immutable i_rmnProxy; /// @dev The immutable flag that indicates if the pool is access-controlled. bool internal immutable i_allowlistEnabled; - /// @dev A set of addresses allowed to trigger lockOrBurn as original senders. - /// Only takes effect if i_allowlistEnabled is true. - /// This can be used to ensure only token-issuer specified addresses can move tokens. - EnumerableSet.AddressSet internal s_allowlist; - /// @dev The address of the router - IRouter internal s_router; - /// @dev A set of allowed chain selectors. We want the allowlist to be enumerable to - /// be able to quickly determine (without parsing logs) who can access the pool. - /// @dev The chain selectors are in uint256 format because of the EnumerableSet implementation. - EnumerableSet.UintSet internal s_remoteChainSelectors; - mapping(uint64 remoteChainSelector => RemoteChainConfig) internal s_remoteChainConfigs; - /// @notice A mapping of hashed pool addresses to their unhashed form. This is used to be able to find the actually - /// configured pools and not just their hashed versions. - mapping(bytes32 poolAddressHash => bytes poolAddress) internal s_remotePoolAddresses; - /// @notice The address of the rate limiter admin. - /// @dev Can be address(0) if none is configured. - address internal s_rateLimitAdmin; + + /// @custom:storage-location erc7201:aave-ccip.storage.UpgradeableTokenPool + struct UpgradeableTokenPoolStorage { + /// @dev A set of addresses allowed to trigger lockOrBurn as original senders. + /// Only takes effect if i_allowlistEnabled is true. + /// This can be used to ensure only token-issuer specified addresses can move tokens. + EnumerableSet.AddressSet s_allowlist; + /// @dev The address of the router + IRouter s_router; + /// @dev A set of allowed chain selectors. We want the allowlist to be enumerable to + /// be able to quickly determine (without parsing logs) who can access the pool. + /// @dev The chain selectors are in uint256 format because of the EnumerableSet implementation. + EnumerableSet.UintSet s_remoteChainSelectors; + mapping(uint64 remoteChainSelector => RemoteChainConfig) s_remoteChainConfigs; + /// @notice A mapping of hashed pool addresses to their unhashed form. This is used to be able to find the actually + /// configured pools and not just their hashed versions. + mapping(bytes32 poolAddressHash => bytes poolAddress) s_remotePoolAddresses; + /// @notice The address of the rate limiter admin. + /// @dev Can be address(0) if none is configured. + address s_rateLimitAdmin; + } + + // keccak256(abi.encode(uint256(keccak256("aave-ccip.storage.UpgradeableTokenPool")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant tokenPoolStorage = 0x1ab3bfa252a45708707caa505d2d48da66a0eaf3681c361d338a45044f67f000; constructor(IERC20 token, uint8 localTokenDecimals, address rmnProxy, bool allowListEnabled) { if (address(token) == address(0) || rmnProxy == address(0)) revert ZeroAddressNotAllowed(); @@ -150,15 +157,16 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @notice Gets the pool's Router /// @return router The pool's Router function getRouter() public view returns (address router) { - return address(s_router); + return address(_getTokenPoolStorage().s_router); } /// @notice Sets the pool's Router /// @param newRouter The new Router function setRouter(address newRouter) public onlyOwner { + UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); if (newRouter == address(0)) revert ZeroAddressNotAllowed(); - address oldRouter = address(s_router); - s_router = IRouter(newRouter); + address oldRouter = address($.s_router); + $.s_router = IRouter(newRouter); emit RouterUpdated(oldRouter, newRouter); } @@ -284,11 +292,13 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @param remoteChainSelector Remote chain selector. /// @dev To support non-evm chains, this value is encoded into bytes function getRemotePools(uint64 remoteChainSelector) public view returns (bytes[] memory) { - bytes32[] memory remotePoolHashes = s_remoteChainConfigs[remoteChainSelector].remotePools.values(); + UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); + + bytes32[] memory remotePoolHashes = $.s_remoteChainConfigs[remoteChainSelector].remotePools.values(); bytes[] memory remotePools = new bytes[](remotePoolHashes.length); for (uint256 i = 0; i < remotePoolHashes.length; ++i) { - remotePools[i] = s_remotePoolAddresses[remotePoolHashes[i]]; + remotePools[i] = $.s_remotePoolAddresses[remotePoolHashes[i]]; } return remotePools; @@ -298,14 +308,17 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @param remoteChainSelector Remote chain selector. /// @param remotePoolAddress The address of the remote pool. function isRemotePool(uint64 remoteChainSelector, bytes calldata remotePoolAddress) public view returns (bool) { - return s_remoteChainConfigs[remoteChainSelector].remotePools.contains(keccak256(remotePoolAddress)); + return + _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].remotePools.contains( + keccak256(remotePoolAddress) + ); } /// @notice Gets the token address on the remote chain. /// @param remoteChainSelector Remote chain selector. /// @dev To support non-evm chains, this value is encoded into bytes function getRemoteToken(uint64 remoteChainSelector) public view returns (bytes memory) { - return s_remoteChainConfigs[remoteChainSelector].remoteTokenAddress; + return _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].remoteTokenAddress; } /// @notice Adds a remote pool for a given chain selector. This could be due to a pool being upgraded on the remote @@ -325,7 +338,9 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { function removeRemotePool(uint64 remoteChainSelector, bytes calldata remotePoolAddress) external onlyOwner { if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector); - if (!s_remoteChainConfigs[remoteChainSelector].remotePools.remove(keccak256(remotePoolAddress))) { + if ( + !_getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].remotePools.remove(keccak256(remotePoolAddress)) + ) { revert InvalidRemotePoolForChain(remoteChainSelector, remotePoolAddress); } @@ -334,13 +349,13 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @inheritdoc IPoolV1 function isSupportedChain(uint64 remoteChainSelector) public view returns (bool) { - return s_remoteChainSelectors.contains(remoteChainSelector); + return _getTokenPoolStorage().s_remoteChainSelectors.contains(remoteChainSelector); } /// @notice Get list of allowed chains /// @return list of chains. function getSupportedChains() public view returns (uint64[] memory) { - uint256[] memory uint256ChainSelectors = s_remoteChainSelectors.values(); + uint256[] memory uint256ChainSelectors = _getTokenPoolStorage().s_remoteChainSelectors.values(); uint64[] memory chainSelectors = new uint64[](uint256ChainSelectors.length); for (uint256 i = 0; i < uint256ChainSelectors.length; ++i) { chainSelectors[i] = uint64(uint256ChainSelectors[i]); @@ -358,20 +373,21 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { uint64[] calldata remoteChainSelectorsToRemove, ChainUpdate[] calldata chainsToAdd ) external virtual onlyOwner { + UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); for (uint256 i = 0; i < remoteChainSelectorsToRemove.length; ++i) { uint64 remoteChainSelectorToRemove = remoteChainSelectorsToRemove[i]; // If the chain doesn't exist, revert - if (!s_remoteChainSelectors.remove(remoteChainSelectorToRemove)) { + if (!$.s_remoteChainSelectors.remove(remoteChainSelectorToRemove)) { revert NonExistentChain(remoteChainSelectorToRemove); } // Remove all remote pool hashes for the chain - bytes32[] memory remotePools = s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.values(); + bytes32[] memory remotePools = $.s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.values(); for (uint256 j = 0; j < remotePools.length; ++j) { - s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.remove(remotePools[j]); + $.s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.remove(remotePools[j]); } - delete s_remoteChainConfigs[remoteChainSelectorToRemove]; + delete $.s_remoteChainConfigs[remoteChainSelectorToRemove]; emit ChainRemoved(remoteChainSelectorToRemove); } @@ -386,11 +402,11 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { } // If the chain already exists, revert - if (!s_remoteChainSelectors.add(newChain.remoteChainSelector)) { + if (!$.s_remoteChainSelectors.add(newChain.remoteChainSelector)) { revert ChainAlreadyExists(newChain.remoteChainSelector); } - RemoteChainConfig storage remoteChainConfig = s_remoteChainConfigs[newChain.remoteChainSelector]; + RemoteChainConfig storage remoteChainConfig = $.s_remoteChainConfigs[newChain.remoteChainSelector]; remoteChainConfig.outboundRateLimiterConfig = RateLimiter.TokenBucket({ rate: newChain.outboundRateLimiterConfig.rate, @@ -425,6 +441,8 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @param remoteChainSelector The remote chain selector for which the remote pool address is being added. /// @param remotePoolAddress The address of the new remote pool. function _setRemotePool(uint64 remoteChainSelector, bytes memory remotePoolAddress) internal { + UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); + if (remotePoolAddress.length == 0) { revert ZeroAddressNotAllowed(); } @@ -432,12 +450,12 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { bytes32 poolHash = keccak256(remotePoolAddress); // Check if the pool already exists. - if (!s_remoteChainConfigs[remoteChainSelector].remotePools.add(poolHash)) { + if (!$.s_remoteChainConfigs[remoteChainSelector].remotePools.add(poolHash)) { revert PoolAlreadyAdded(remoteChainSelector, remotePoolAddress); } // Add the pool to the mapping to be able to un-hash it later. - s_remotePoolAddresses[poolHash] = remotePoolAddress; + $.s_remotePoolAddresses[poolHash] = remotePoolAddress; emit RemotePoolAdded(remoteChainSelector, remotePoolAddress); } @@ -468,23 +486,29 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @dev Only callable by the owner. /// @param rateLimitAdmin The new rate limiter admin address. function setRateLimitAdmin(address rateLimitAdmin) external onlyOwner { - s_rateLimitAdmin = rateLimitAdmin; + _getTokenPoolStorage().s_rateLimitAdmin = rateLimitAdmin; emit RateLimitAdminSet(rateLimitAdmin); } /// @notice Gets the rate limiter admin address. function getRateLimitAdmin() external view returns (address) { - return s_rateLimitAdmin; + return _getTokenPoolStorage().s_rateLimitAdmin; } /// @notice Consumes outbound rate limiting capacity in this pool function _consumeOutboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal { - s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._consume(amount, address(i_token)); + _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._consume( + amount, + address(i_token) + ); } /// @notice Consumes inbound rate limiting capacity in this pool function _consumeInboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal { - s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._consume(amount, address(i_token)); + _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._consume( + amount, + address(i_token) + ); } /// @notice Gets the token bucket with its values for the block it was requested at. @@ -492,7 +516,11 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { function getCurrentOutboundRateLimiterState( uint64 remoteChainSelector ) external view returns (RateLimiter.TokenBucket memory) { - return s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._currentTokenBucketState(); + return + _getTokenPoolStorage() + .s_remoteChainConfigs[remoteChainSelector] + .outboundRateLimiterConfig + ._currentTokenBucketState(); } /// @notice Gets the token bucket with its values for the block it was requested at. @@ -500,7 +528,11 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { function getCurrentInboundRateLimiterState( uint64 remoteChainSelector ) external view returns (RateLimiter.TokenBucket memory) { - return s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._currentTokenBucketState(); + return + _getTokenPoolStorage() + .s_remoteChainConfigs[remoteChainSelector] + .inboundRateLimiterConfig + ._currentTokenBucketState(); } /// @notice Sets the chain rate limiter config. @@ -512,7 +544,7 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { RateLimiter.Config memory outboundConfig, RateLimiter.Config memory inboundConfig ) external { - if (msg.sender != s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + if (msg.sender != _getTokenPoolStorage().s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); } @@ -524,9 +556,13 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { ) internal { if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector); RateLimiter._validateTokenBucketConfig(outboundConfig, false); - s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._setTokenBucketConfig(outboundConfig); + _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._setTokenBucketConfig( + outboundConfig + ); RateLimiter._validateTokenBucketConfig(inboundConfig, false); - s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._setTokenBucketConfig(inboundConfig); + _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._setTokenBucketConfig( + inboundConfig + ); emit ChainConfigured(remoteChainSelector, outboundConfig, inboundConfig); } @@ -538,14 +574,16 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// is a permissioned onRamp for the given chain on the Router. function _onlyOnRamp(uint64 remoteChainSelector) internal view { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); + if (!(msg.sender == _getTokenPoolStorage().s_router.getOnRamp(remoteChainSelector))) + revert CallerIsNotARampOnRouter(msg.sender); } /// @notice Checks whether remote chain selector is configured on this contract, and if the msg.sender /// is a permissioned offRamp for the given chain on the Router. function _onlyOffRamp(uint64 remoteChainSelector) internal view { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!s_router.isOffRamp(remoteChainSelector, msg.sender)) revert CallerIsNotARampOnRouter(msg.sender); + if (!_getTokenPoolStorage().s_router.isOffRamp(remoteChainSelector, msg.sender)) + revert CallerIsNotARampOnRouter(msg.sender); } // ================================================================ @@ -554,7 +592,7 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { function _checkAllowList(address sender) internal view { if (i_allowlistEnabled) { - if (!s_allowlist.contains(sender)) { + if (!_getTokenPoolStorage().s_allowlist.contains(sender)) { revert SenderNotAllowed(sender); } } @@ -569,7 +607,7 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @notice Gets the allowed addresses. /// @return The allowed addresses. function getAllowList() external view returns (address[] memory) { - return s_allowlist.values(); + return _getTokenPoolStorage().s_allowlist.values(); } /// @notice Apply updates to the allow list. @@ -582,10 +620,10 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { /// @notice Internal version of applyAllowListUpdates to allow for reuse in the constructor. function _applyAllowListUpdates(address[] memory removes, address[] memory adds) internal { if (!i_allowlistEnabled) revert AllowListNotEnabled(); - + UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); for (uint256 i = 0; i < removes.length; ++i) { address toRemove = removes[i]; - if (s_allowlist.remove(toRemove)) { + if ($.s_allowlist.remove(toRemove)) { emit AllowListRemove(toRemove); } } @@ -594,13 +632,15 @@ abstract contract UpgradeableTokenPool is IPoolV1, Ownable2StepMsgSender { if (toAdd == address(0)) { continue; } - if (s_allowlist.add(toAdd)) { + if ($.s_allowlist.add(toAdd)) { emit AllowListAdd(toAdd); } } } - /// @dev This empty reserved space is put in place to allow future versions to add new - /// variables without shifting down storage in the inheritance chain. - uint256[42] private __gap; + function _getTokenPoolStorage() internal pure returns (UpgradeableTokenPoolStorage storage $) { + assembly ("memory-safe") { + $.slot := tokenPoolStorage + } + } } 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 782b7e7a8a..cb305c9351 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 30203a4ced..80ef621eff 100644 +index 30203a4ced..5f1480d301 100644 --- a/src/v0.8/ccip/pools/BurnMintTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableBurnMintTokenPool.sol @@ -1,33 +1,74 @@ @@ -61,7 +61,7 @@ index 30203a4ced..80ef621eff 100644 + if (router == address(0) || owner_ == address(0)) revert ZeroAddressNotAllowed(); + + _transferOwnership(owner_); -+ s_router = IRouter(router); ++ _getTokenPoolStorage().s_router = IRouter(router); + if (i_allowlistEnabled) _applyAllowListUpdates(new address[](0), allowlist); + } + 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 a3aad47ffe..386ba1a8e2 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 ecc28a14dd..5d5e055299 100644 +index ecc28a14dd..feb142b89e 100644 --- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol @@ -1,25 +1,45 @@ @@ -59,18 +59,29 @@ index ecc28a14dd..5d5e055299 100644 event LiquidityTransferred(address indexed from, uint256 amount); -@@ -33,30 +53,69 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion - /// @notice The address of the rebalancer. - address internal s_rebalancer; +@@ -30,33 +50,80 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion + /// 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 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; ++ /// @custom:storage-location erc7201:aave-ccip.storage.UpgradeableLockReleaseTokenPool ++ struct UpgradeableLockReleaseTokenPoolStorage { ++ /// @notice The address of the rebalancer. ++ address s_rebalancer; ++ /// @notice Maximum amount of tokens that can be bridged to other chains ++ uint256 s_bridgeLimit; ++ /// @notice Amount of tokens bridged (transferred out) ++ /// @dev Must always be equal to or below the bridge limit ++ uint256 s_currentBridged; ++ /// @notice The address of the bridge limit admin. ++ /// @dev Can be address(0) if none is configured. ++ address s_bridgeLimitAdmin; ++ } ++ ++ // bytes32 private constant lockReleasePoolStorage = keccak256(abi.encode(uint256(keccak256("aave-ccip.storage.UpgradeableLockReleaseTokenPool")) - 1)) & ~bytes32(uint256(0xff)) ++ bytes32 private constant lockReleasePoolStorage = 0x0f06852668cdd7d8554206875bc1ed67644f9d2a4b038c34789cc8b26cb42300; + + // @notice Constructor + // @param token The bridgeable token that is managed by this pool. @@ -109,9 +120,9 @@ index ecc28a14dd..5d5e055299 100644 + if (router == address(0) || owner_ == address(0)) revert ZeroAddressNotAllowed(); + + _transferOwnership(owner_); -+ s_router = IRouter(router); ++ _getTokenPoolStorage().s_router = IRouter(router); + if (i_allowlistEnabled) _applyAllowListUpdates(new address[](0), allowlist); -+ s_bridgeLimit = bridgeLimit; ++ _getLockReleasePoolStorage().s_bridgeLimit = bridgeLimit; + } + /// @notice Locks the token in the pool @@ -119,8 +130,10 @@ index ecc28a14dd..5d5e055299 100644 function lockOrBurn( Pool.LockOrBurnInV1 calldata lockOrBurnIn ) external virtual override returns (Pool.LockOrBurnOutV1 memory) { ++ UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); ++ + // Increase bridged amount because tokens are leaving the source chain -+ if ((s_currentBridged += lockOrBurnIn.amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit); ++ if (($.s_currentBridged += lockOrBurnIn.amount) > $.s_bridgeLimit) revert BridgeLimitExceeded($.s_bridgeLimit); + _validateLockOrBurn(lockOrBurnIn); @@ -138,14 +151,16 @@ index ecc28a14dd..5d5e055299 100644 } /// @notice Release tokens from the pool to the recipient -@@ -64,11 +123,18 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -64,11 +131,20 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion function releaseOrMint( Pool.ReleaseOrMintInV1 calldata releaseOrMintIn ) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) { ++ UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); ++ + // This should never occur. Amount should never exceed the current bridged amount -+ if (releaseOrMintIn.amount > s_currentBridged) revert NotEnoughBridgedAmount(); ++ if (releaseOrMintIn.amount > $.s_currentBridged) revert NotEnoughBridgedAmount(); + // Reduce bridged amount because tokens are back to source chain -+ s_currentBridged -= releaseOrMintIn.amount; ++ $.s_currentBridged -= releaseOrMintIn.amount; + _validateReleaseOrMint(releaseOrMintIn); @@ -159,7 +174,7 @@ index ecc28a14dd..5d5e055299 100644 // Release to the recipient getToken().safeTransfer(releaseOrMintIn.receiver, localAmount); -@@ -79,9 +145,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -79,24 +155,69 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion } /// @inheritdoc IERC165 @@ -170,23 +185,29 @@ index ecc28a14dd..5d5e055299 100644 return interfaceId == type(ILiquidityContainer).interfaceId || super.supportsInterface(interfaceId); } -@@ -93,12 +157,55 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion + /// @notice Gets LiquidityManager, can be address(0) if none is configured. + /// @return The current liquidity manager. + function getRebalancer() external view returns (address) { +- return s_rebalancer; ++ return _getLockReleasePoolStorage().s_rebalancer; + } /// @notice Sets the LiquidityManager address. /// @dev Only callable by the owner. - function setRebalancer( - address rebalancer - ) external onlyOwner { +- s_rebalancer = rebalancer; + function setRebalancer(address rebalancer) external onlyOwner { - s_rebalancer = rebalancer; - } - ++ _getLockReleasePoolStorage().s_rebalancer = rebalancer; ++ } ++ + /// @notice Sets the current bridged amount to other chains + /// @dev Only callable by the owner. + /// @dev Does not emit event, it is expected to only be called during token pool migrations. + /// @param newCurrentBridged The new bridged amount + function setCurrentBridgedAmount(uint256 newCurrentBridged) external onlyOwner { -+ s_currentBridged = newCurrentBridged; ++ _getLockReleasePoolStorage().s_currentBridged = newCurrentBridged; + } + + /// @notice Sets the bridge limit, the maximum amount of tokens that can be bridged out @@ -194,9 +215,11 @@ index ecc28a14dd..5d5e055299 100644 + /// @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; ++ UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); ++ ++ if (msg.sender != $.s_bridgeLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); ++ uint256 oldBridgeLimit = $.s_bridgeLimit; ++ $.s_bridgeLimit = newBridgeLimit; + emit BridgeLimitUpdated(oldBridgeLimit, newBridgeLimit); + } + @@ -204,32 +227,32 @@ index ecc28a14dd..5d5e055299 100644 + /// @dev Only callable by the owner. + /// @param bridgeLimitAdmin The new bridge limit admin address. + function setBridgeLimitAdmin(address bridgeLimitAdmin) external onlyOwner { -+ address oldAdmin = s_bridgeLimitAdmin; -+ s_bridgeLimitAdmin = bridgeLimitAdmin; ++ UpgradeableLockReleaseTokenPoolStorage storage $ = _getLockReleasePoolStorage(); ++ ++ address oldAdmin = $.s_bridgeLimitAdmin; ++ $.s_bridgeLimitAdmin = bridgeLimitAdmin; + emit BridgeLimitAdminUpdated(oldAdmin, 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; ++ return _getLockReleasePoolStorage().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; ++ return _getLockReleasePoolStorage().s_currentBridged; + } + + /// @notice Gets the bridge limiter admin address. + function getBridgeLimitAdmin() external view returns (address) { -+ return s_bridgeLimitAdmin; -+ } -+ ++ return _getLockReleasePoolStorage().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) { -@@ -107,9 +214,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion +@@ -107,11 +228,9 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @notice Adds liquidity to the pool. The tokens should be approved first. /// @param amount The amount of liquidity to provide. @@ -238,20 +261,25 @@ index ecc28a14dd..5d5e055299 100644 - ) external { + function provideLiquidity(uint256 amount) external { if (!i_acceptLiquidity) revert LiquidityNotAccepted(); - if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender); +- if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender); ++ if (_getLockReleasePoolStorage().s_rebalancer != msg.sender) revert Unauthorized(msg.sender); -@@ -119,9 +224,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion + i_token.safeTransferFrom(msg.sender, address(this), amount); + emit LiquidityAdded(msg.sender, amount); +@@ -119,10 +238,8 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @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); + function withdrawLiquidity(uint256 amount) external { - if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender); ++ if (_getLockReleasePoolStorage().s_rebalancer != msg.sender) revert Unauthorized(msg.sender); if (i_token.balanceOf(address(this)) < amount) revert InsufficientLiquidity(); -@@ -141,7 +244,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion + i_token.safeTransfer(msg.sender, amount); +@@ -141,8 +258,14 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion /// @param from The address of the old pool. /// @param amount The amount of liquidity to transfer. function transferLiquidity(address from, uint256 amount) external onlyOwner { @@ -260,4 +288,11 @@ index ecc28a14dd..5d5e055299 100644 emit LiquidityTransferred(from, amount); } ++ ++ function _getLockReleasePoolStorage() internal pure returns (UpgradeableLockReleaseTokenPoolStorage storage $) { ++ assembly ("memory-safe") { ++ $.slot := lockReleasePoolStorage ++ } ++ } + } ``` 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 c1f8beed95..a5ecafbc8d 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 cd3096f4ef..8c0965a67f 100644 +index cd3096f4ef..d0eca208e3 100644 --- a/src/v0.8/ccip/pools/TokenPool.sol +++ b/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol @@ -1,26 +1,29 @@ @@ -55,12 +55,54 @@ index cd3096f4ef..8c0965a67f 100644 using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; -@@ -117,34 +120,18 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { - /// @dev Can be address(0) if none is configured. - address internal s_rateLimitAdmin; +@@ -99,52 +102,43 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + address internal immutable i_rmnProxy; + /// @dev The immutable flag that indicates if the pool is access-controlled. + bool internal immutable i_allowlistEnabled; +- /// @dev A set of addresses allowed to trigger lockOrBurn as original senders. +- /// Only takes effect if i_allowlistEnabled is true. +- /// This can be used to ensure only token-issuer specified addresses can move tokens. +- EnumerableSet.AddressSet internal s_allowlist; +- /// @dev The address of the router +- IRouter internal s_router; +- /// @dev A set of allowed chain selectors. We want the allowlist to be enumerable to +- /// be able to quickly determine (without parsing logs) who can access the pool. +- /// @dev The chain selectors are in uint256 format because of the EnumerableSet implementation. +- EnumerableSet.UintSet internal s_remoteChainSelectors; +- mapping(uint64 remoteChainSelector => RemoteChainConfig) internal s_remoteChainConfigs; +- /// @notice A mapping of hashed pool addresses to their unhashed form. This is used to be able to find the actually +- /// configured pools and not just their hashed versions. +- mapping(bytes32 poolAddressHash => bytes poolAddress) internal s_remotePoolAddresses; +- /// @notice The address of the rate limiter admin. +- /// @dev Can be address(0) if none is configured. +- address internal s_rateLimitAdmin; - constructor(IERC20 token, uint8 localTokenDecimals, address[] memory allowlist, address rmnProxy, address router) { - if (address(token) == address(0) || router == address(0) || rmnProxy == address(0)) revert ZeroAddressNotAllowed(); ++ /// @custom:storage-location erc7201:aave-ccip.storage.UpgradeableTokenPool ++ struct UpgradeableTokenPoolStorage { ++ /// @dev A set of addresses allowed to trigger lockOrBurn as original senders. ++ /// Only takes effect if i_allowlistEnabled is true. ++ /// This can be used to ensure only token-issuer specified addresses can move tokens. ++ EnumerableSet.AddressSet s_allowlist; ++ /// @dev The address of the router ++ IRouter s_router; ++ /// @dev A set of allowed chain selectors. We want the allowlist to be enumerable to ++ /// be able to quickly determine (without parsing logs) who can access the pool. ++ /// @dev The chain selectors are in uint256 format because of the EnumerableSet implementation. ++ EnumerableSet.UintSet s_remoteChainSelectors; ++ mapping(uint64 remoteChainSelector => RemoteChainConfig) s_remoteChainConfigs; ++ /// @notice A mapping of hashed pool addresses to their unhashed form. This is used to be able to find the actually ++ /// configured pools and not just their hashed versions. ++ mapping(bytes32 poolAddressHash => bytes poolAddress) s_remotePoolAddresses; ++ /// @notice The address of the rate limiter admin. ++ /// @dev Can be address(0) if none is configured. ++ address s_rateLimitAdmin; ++ } ++ ++ // keccak256(abi.encode(uint256(keccak256("aave-ccip.storage.UpgradeableTokenPool")) - 1)) & ~bytes32(uint256(0xff)) ++ bytes32 private constant tokenPoolStorage = 0x1ab3bfa252a45708707caa505d2d48da66a0eaf3681c361d338a45044f67f000; ++ + constructor(IERC20 token, uint8 localTokenDecimals, address rmnProxy, bool allowListEnabled) { + if (address(token) == address(0) || rmnProxy == address(0)) revert ZeroAddressNotAllowed(); i_token = token; @@ -94,7 +136,13 @@ index cd3096f4ef..8c0965a67f 100644 return token == address(i_token); } -@@ -168,9 +155,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { +@@ -163,27 +157,26 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + /// @notice Gets the pool's Router + /// @return router The pool's Router + function getRouter() public view returns (address router) { +- return address(s_router); ++ return address(_getTokenPoolStorage().s_router); + } /// @notice Sets the pool's Router /// @param newRouter The new Router @@ -102,10 +150,14 @@ index cd3096f4ef..8c0965a67f 100644 - address newRouter - ) public onlyOwner { + function setRouter(address newRouter) public onlyOwner { ++ UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); if (newRouter == address(0)) revert ZeroAddressNotAllowed(); - address oldRouter = address(s_router); - s_router = IRouter(newRouter); -@@ -179,11 +164,11 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { +- address oldRouter = address(s_router); +- s_router = IRouter(newRouter); ++ address oldRouter = address($.s_router); ++ $.s_router = IRouter(newRouter); + + emit RouterUpdated(oldRouter, newRouter); } /// @notice Signals which version of the pool interface is supported @@ -122,7 +174,7 @@ index cd3096f4ef..8c0965a67f 100644 } // ================================================================ -@@ -199,9 +184,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { +@@ -199,9 +192,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { /// @param lockOrBurnIn The input to validate. /// @dev This function should always be called before executing a lock or burn. Not doing so would allow /// for various exploits. @@ -133,7 +185,7 @@ index cd3096f4ef..8c0965a67f 100644 if (!isSupportedToken(lockOrBurnIn.localToken)) revert InvalidToken(lockOrBurnIn.localToken); if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(lockOrBurnIn.remoteChainSelector)))) revert CursedByRMN(); _checkAllowList(lockOrBurnIn.originalSender); -@@ -219,9 +202,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { +@@ -219,9 +210,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { /// @param releaseOrMintIn The input to validate. /// @dev This function should always be called before executing a release or mint. Not doing so would allow /// for various exploits. @@ -144,7 +196,7 @@ index cd3096f4ef..8c0965a67f 100644 if (!isSupportedToken(releaseOrMintIn.localToken)) revert InvalidToken(releaseOrMintIn.localToken); if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(releaseOrMintIn.remoteChainSelector)))) revert CursedByRMN(); _onlyOffRamp(releaseOrMintIn.remoteChainSelector); -@@ -247,9 +228,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { +@@ -247,9 +236,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { return abi.encode(i_tokenDecimals); } @@ -155,40 +207,81 @@ index cd3096f4ef..8c0965a67f 100644 // Fallback to the local token decimals if the source pool data is empty. This allows for backwards compatibility. if (sourcePoolData.length == 0) { return i_tokenDecimals; -@@ -304,9 +283,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { +@@ -304,14 +291,14 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { /// @notice Gets the pool address on the remote chain. /// @param remoteChainSelector Remote chain selector. /// @dev To support non-evm chains, this value is encoded into bytes - function getRemotePools( - uint64 remoteChainSelector - ) public view returns (bytes[] memory) { +- bytes32[] memory remotePoolHashes = s_remoteChainConfigs[remoteChainSelector].remotePools.values(); + function getRemotePools(uint64 remoteChainSelector) public view returns (bytes[] memory) { - bytes32[] memory remotePoolHashes = s_remoteChainConfigs[remoteChainSelector].remotePools.values(); ++ UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); ++ ++ bytes32[] memory remotePoolHashes = $.s_remoteChainConfigs[remoteChainSelector].remotePools.values(); bytes[] memory remotePools = new bytes[](remotePoolHashes.length); -@@ -327,9 +304,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + for (uint256 i = 0; i < remotePoolHashes.length; ++i) { +- remotePools[i] = s_remotePoolAddresses[remotePoolHashes[i]]; ++ remotePools[i] = $.s_remotePoolAddresses[remotePoolHashes[i]]; + } + + return remotePools; +@@ -321,16 +308,17 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + /// @param remoteChainSelector Remote chain selector. + /// @param remotePoolAddress The address of the remote pool. + function isRemotePool(uint64 remoteChainSelector, bytes calldata remotePoolAddress) public view returns (bool) { +- return s_remoteChainConfigs[remoteChainSelector].remotePools.contains(keccak256(remotePoolAddress)); ++ return ++ _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].remotePools.contains( ++ keccak256(remotePoolAddress) ++ ); + } + /// @notice Gets the token address on the remote chain. /// @param remoteChainSelector Remote chain selector. /// @dev To support non-evm chains, this value is encoded into bytes - function getRemoteToken( - uint64 remoteChainSelector - ) public view returns (bytes memory) { +- return s_remoteChainConfigs[remoteChainSelector].remoteTokenAddress; + function getRemoteToken(uint64 remoteChainSelector) public view returns (bytes memory) { - return s_remoteChainConfigs[remoteChainSelector].remoteTokenAddress; ++ return _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].remoteTokenAddress; } -@@ -358,9 +333,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + /// @notice Adds a remote pool for a given chain selector. This could be due to a pool being upgraded on the remote +@@ -350,7 +338,9 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + function removeRemotePool(uint64 remoteChainSelector, bytes calldata remotePoolAddress) external onlyOwner { + if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector); + +- if (!s_remoteChainConfigs[remoteChainSelector].remotePools.remove(keccak256(remotePoolAddress))) { ++ if ( ++ !_getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].remotePools.remove(keccak256(remotePoolAddress)) ++ ) { + revert InvalidRemotePoolForChain(remoteChainSelector, remotePoolAddress); + } + +@@ -358,16 +348,14 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { } /// @inheritdoc IPoolV1 - function isSupportedChain( - uint64 remoteChainSelector - ) public view returns (bool) { +- return s_remoteChainSelectors.contains(remoteChainSelector); + function isSupportedChain(uint64 remoteChainSelector) public view returns (bool) { - return s_remoteChainSelectors.contains(remoteChainSelector); ++ return _getTokenPoolStorage().s_remoteChainSelectors.contains(remoteChainSelector); } -@@ -379,8 +352,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + /// @notice Get list of allowed chains + /// @return list of chains. + function getSupportedChains() public view returns (uint64[] memory) { +- uint256[] memory uint256ChainSelectors = s_remoteChainSelectors.values(); ++ uint256[] memory uint256ChainSelectors = _getTokenPoolStorage().s_remoteChainSelectors.values(); + uint64[] memory chainSelectors = new uint64[](uint256ChainSelectors.length); + for (uint256 i = 0; i < uint256ChainSelectors.length; ++i) { + chainSelectors[i] = uint64(uint256ChainSelectors[i]); +@@ -379,27 +367,27 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { /// @notice Sets the permissions for a list of chains selectors. Actual senders for these chains /// need to be allowed on the Router to interact with this pool. /// @param remoteChainSelectorsToRemove A list of chain selectors to remove. @@ -198,18 +291,158 @@ index cd3096f4ef..8c0965a67f 100644 /// @dev Only callable by the owner function applyChainUpdates( uint64[] calldata remoteChainSelectorsToRemove, -@@ -495,9 +467,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + ChainUpdate[] calldata chainsToAdd + ) external virtual onlyOwner { ++ UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); + for (uint256 i = 0; i < remoteChainSelectorsToRemove.length; ++i) { + uint64 remoteChainSelectorToRemove = remoteChainSelectorsToRemove[i]; + // If the chain doesn't exist, revert +- if (!s_remoteChainSelectors.remove(remoteChainSelectorToRemove)) { ++ if (!$.s_remoteChainSelectors.remove(remoteChainSelectorToRemove)) { + revert NonExistentChain(remoteChainSelectorToRemove); + } + + // Remove all remote pool hashes for the chain +- bytes32[] memory remotePools = s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.values(); ++ bytes32[] memory remotePools = $.s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.values(); + for (uint256 j = 0; j < remotePools.length; ++j) { +- s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.remove(remotePools[j]); ++ $.s_remoteChainConfigs[remoteChainSelectorToRemove].remotePools.remove(remotePools[j]); + } + +- delete s_remoteChainConfigs[remoteChainSelectorToRemove]; ++ delete $.s_remoteChainConfigs[remoteChainSelectorToRemove]; + + emit ChainRemoved(remoteChainSelectorToRemove); + } +@@ -414,11 +402,11 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + } + + // If the chain already exists, revert +- if (!s_remoteChainSelectors.add(newChain.remoteChainSelector)) { ++ if (!$.s_remoteChainSelectors.add(newChain.remoteChainSelector)) { + revert ChainAlreadyExists(newChain.remoteChainSelector); + } + +- RemoteChainConfig storage remoteChainConfig = s_remoteChainConfigs[newChain.remoteChainSelector]; ++ RemoteChainConfig storage remoteChainConfig = $.s_remoteChainConfigs[newChain.remoteChainSelector]; + + remoteChainConfig.outboundRateLimiterConfig = RateLimiter.TokenBucket({ + rate: newChain.outboundRateLimiterConfig.rate, +@@ -453,6 +441,8 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + /// @param remoteChainSelector The remote chain selector for which the remote pool address is being added. + /// @param remotePoolAddress The address of the new remote pool. + function _setRemotePool(uint64 remoteChainSelector, bytes memory remotePoolAddress) internal { ++ UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); ++ + if (remotePoolAddress.length == 0) { + revert ZeroAddressNotAllowed(); + } +@@ -460,12 +450,12 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + bytes32 poolHash = keccak256(remotePoolAddress); + + // Check if the pool already exists. +- if (!s_remoteChainConfigs[remoteChainSelector].remotePools.add(poolHash)) { ++ if (!$.s_remoteChainConfigs[remoteChainSelector].remotePools.add(poolHash)) { + revert PoolAlreadyAdded(remoteChainSelector, remotePoolAddress); + } + + // Add the pool to the mapping to be able to un-hash it later. +- s_remotePoolAddresses[poolHash] = remotePoolAddress; ++ $.s_remotePoolAddresses[poolHash] = remotePoolAddress; + + emit RemotePoolAdded(remoteChainSelector, remotePoolAddress); + } +@@ -495,26 +485,30 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { /// @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; + function setRateLimitAdmin(address rateLimitAdmin) external onlyOwner { - s_rateLimitAdmin = rateLimitAdmin; ++ _getTokenPoolStorage().s_rateLimitAdmin = rateLimitAdmin; emit RateLimitAdminSet(rateLimitAdmin); } -@@ -566,18 +536,14 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + + /// @notice Gets the rate limiter admin address. + function getRateLimitAdmin() external view returns (address) { +- return s_rateLimitAdmin; ++ return _getTokenPoolStorage().s_rateLimitAdmin; + } + + /// @notice Consumes outbound rate limiting capacity in this pool + function _consumeOutboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal { +- s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._consume(amount, address(i_token)); ++ _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._consume( ++ amount, ++ address(i_token) ++ ); + } + + /// @notice Consumes inbound rate limiting capacity in this pool + function _consumeInboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal { +- s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._consume(amount, address(i_token)); ++ _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._consume( ++ amount, ++ address(i_token) ++ ); + } + + /// @notice Gets the token bucket with its values for the block it was requested at. +@@ -522,7 +516,11 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + function getCurrentOutboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (RateLimiter.TokenBucket memory) { +- return s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._currentTokenBucketState(); ++ return ++ _getTokenPoolStorage() ++ .s_remoteChainConfigs[remoteChainSelector] ++ .outboundRateLimiterConfig ++ ._currentTokenBucketState(); + } + + /// @notice Gets the token bucket with its values for the block it was requested at. +@@ -530,7 +528,11 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + function getCurrentInboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (RateLimiter.TokenBucket memory) { +- return s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._currentTokenBucketState(); ++ return ++ _getTokenPoolStorage() ++ .s_remoteChainConfigs[remoteChainSelector] ++ .inboundRateLimiterConfig ++ ._currentTokenBucketState(); + } + + /// @notice Sets the chain rate limiter config. +@@ -542,7 +544,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + RateLimiter.Config memory outboundConfig, + RateLimiter.Config memory inboundConfig + ) external { +- if (msg.sender != s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); ++ if (msg.sender != _getTokenPoolStorage().s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender); + + _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); + } +@@ -554,9 +556,13 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + ) internal { + if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector); + RateLimiter._validateTokenBucketConfig(outboundConfig, false); +- s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._setTokenBucketConfig(outboundConfig); ++ _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._setTokenBucketConfig( ++ outboundConfig ++ ); + RateLimiter._validateTokenBucketConfig(inboundConfig, false); +- s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._setTokenBucketConfig(inboundConfig); ++ _getTokenPoolStorage().s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._setTokenBucketConfig( ++ inboundConfig ++ ); + emit ChainConfigured(remoteChainSelector, outboundConfig, inboundConfig); + } + +@@ -566,31 +572,27 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { /// @notice Checks whether remote chain selector is configured on this contract, and if the msg.sender /// is a permissioned onRamp for the given chain on the Router. @@ -218,7 +451,9 @@ index cd3096f4ef..8c0965a67f 100644 - ) internal view { + function _onlyOnRamp(uint64 remoteChainSelector) internal view { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); +- if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); ++ if (!(msg.sender == _getTokenPoolStorage().s_router.getOnRamp(remoteChainSelector))) ++ revert CallerIsNotARampOnRouter(msg.sender); } /// @notice Checks whether remote chain selector is configured on this contract, and if the msg.sender @@ -228,9 +463,12 @@ index cd3096f4ef..8c0965a67f 100644 - ) internal view { + function _onlyOffRamp(uint64 remoteChainSelector) internal view { if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!s_router.isOffRamp(remoteChainSelector, msg.sender)) revert CallerIsNotARampOnRouter(msg.sender); +- if (!s_router.isOffRamp(remoteChainSelector, msg.sender)) revert CallerIsNotARampOnRouter(msg.sender); ++ if (!_getTokenPoolStorage().s_router.isOffRamp(remoteChainSelector, msg.sender)) ++ revert CallerIsNotARampOnRouter(msg.sender); } -@@ -586,9 +552,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + + // ================================================================ // │ Allowlist │ // ================================================================ @@ -239,16 +477,48 @@ index cd3096f4ef..8c0965a67f 100644 - ) internal view { + function _checkAllowList(address sender) internal view { if (i_allowlistEnabled) { - if (!s_allowlist.contains(sender)) { +- if (!s_allowlist.contains(sender)) { ++ if (!_getTokenPoolStorage().s_allowlist.contains(sender)) { revert SenderNotAllowed(sender); -@@ -635,4 +599,8 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + } + } +@@ -605,7 +607,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + /// @notice Gets the allowed addresses. + /// @return The allowed addresses. + function getAllowList() external view returns (address[] memory) { +- return s_allowlist.values(); ++ return _getTokenPoolStorage().s_allowlist.values(); + } + + /// @notice Apply updates to the allow list. +@@ -618,10 +620,10 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + /// @notice Internal version of applyAllowListUpdates to allow for reuse in the constructor. + function _applyAllowListUpdates(address[] memory removes, address[] memory adds) internal { + if (!i_allowlistEnabled) revert AllowListNotEnabled(); +- ++ UpgradeableTokenPoolStorage storage $ = _getTokenPoolStorage(); + for (uint256 i = 0; i < removes.length; ++i) { + address toRemove = removes[i]; +- if (s_allowlist.remove(toRemove)) { ++ if ($.s_allowlist.remove(toRemove)) { + emit AllowListRemove(toRemove); + } + } +@@ -630,9 +632,15 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { + if (toAdd == address(0)) { + continue; + } +- if (s_allowlist.add(toAdd)) { ++ if ($.s_allowlist.add(toAdd)) { + emit AllowListAdd(toAdd); } } } + -+ /// @dev This empty reserved space is put in place to allow future versions to add new -+ /// variables without shifting down storage in the inheritance chain. -+ uint256[42] private __gap; ++ function _getTokenPoolStorage() internal pure returns (UpgradeableTokenPoolStorage storage $) { ++ assembly ("memory-safe") { ++ $.slot := tokenPoolStorage ++ } ++ } } - ```