diff --git a/src/core/Bootstrap.sol b/src/core/Bootstrap.sol index 68fa1285..05a65408 100644 --- a/src/core/Bootstrap.sol +++ b/src/core/Bootstrap.sol @@ -12,7 +12,6 @@ import {OAppCoreUpgradeable} from "../lzApp/OAppCoreUpgradeable.sol"; import {ILSTRestakingController} from "../interfaces/ILSTRestakingController.sol"; import {ICustomProxyAdmin} from "../interfaces/ICustomProxyAdmin.sol"; import {IOperatorRegistry} from "../interfaces/IOperatorRegistry.sol"; -import {ITokenWhitelister} from "../interfaces/ITokenWhitelister.sol"; import {IVault} from "../interfaces/IVault.sol"; import {BootstrapLzReceiver} from "./BootstrapLzReceiver.sol"; @@ -29,7 +28,6 @@ contract Bootstrap is Initializable, PausableUpgradeable, OwnableUpgradeable, - ITokenWhitelister, ILSTRestakingController, IOperatorRegistry, BootstrapLzReceiver @@ -160,34 +158,13 @@ contract Bootstrap is } // implementation of ITokenWhitelister - function addWhitelistToken(address _token) external beforeLocked onlyOwner whenNotPaused { - require(!isWhitelistedToken[_token], "Bootstrap: token should be not whitelisted before"); - whitelistTokens.push(_token); - isWhitelistedToken[_token] = true; - - // deploy the corresponding vault if not deployed before - if (address(tokenToVault[_token]) == address(0)) { - _deployVault(_token); - } - - emit WhitelistTokenAdded(_token); + function addWhitelistToken(address _token) public override beforeLocked onlyOwner whenNotPaused { + super.addWhitelistToken(_token); } // implementation of ITokenWhitelister - function removeWhitelistToken(address _token) external beforeLocked onlyOwner whenNotPaused { - require(isWhitelistedToken[_token], "Bootstrap: token should be already whitelisted"); - isWhitelistedToken[_token] = false; - // the implicit assumption here is that the _token must be included in whitelistTokens - // if isWhitelistedToken[_token] is true - for (uint i = 0; i < whitelistTokens.length; i++) { - if (whitelistTokens[i] == _token) { - whitelistTokens[i] = whitelistTokens[whitelistTokens.length - 1]; - whitelistTokens.pop(); - break; - } - } - - emit WhitelistTokenRemoved(_token); + function removeWhitelistToken(address _token) public override beforeLocked onlyOwner whenNotPaused isTokenWhitelisted(_token) { + super.removeWhitelistToken(_token); } // implementation of IOperatorRegistry @@ -328,23 +305,9 @@ contract Bootstrap is emit OperatorCommissionUpdated(newRate); } - /** - * @notice Validates the inputs and returns the vault for the given token. - * @param token The adddress of the token. - * @param amount The amount of the token. - * @dev This function checks if the token is whitelisted, the amount is greater than zero - * and that a vault for the token exists. - */ - function _validateAndGetVault(address token, uint256 amount) internal view returns (IVault) { - require(isWhitelistedToken[token], "Bootstrap: token is not whitelisted"); - require(amount > 0, "Bootstrap: amount should be greater than zero"); - - return _getVault(token); - } - // implementation of IController - function deposit(address token, uint256 amount) external payable override beforeLocked whenNotPaused { - IVault vault = _validateAndGetVault(token, amount); + function deposit(address token, uint256 amount) external payable override beforeLocked whenNotPaused isTokenWhitelisted(token) isValidAmount(amount) { + IVault vault = _getVault(token); vault.deposit(msg.sender, amount); if (!isDepositor[msg.sender]) { @@ -370,8 +333,8 @@ contract Bootstrap is function withdrawPrincipleFromExocore( address token, uint256 amount - ) external payable override beforeLocked whenNotPaused { - IVault vault = _validateAndGetVault(token, amount); + ) external payable override beforeLocked whenNotPaused isTokenWhitelisted(token) isValidAmount(amount) { + IVault vault = _getVault(token); uint256 deposited = totalDepositAmounts[msg.sender][token]; require(deposited >= amount, "Bootstrap: insufficient deposited balance"); @@ -397,8 +360,8 @@ contract Bootstrap is } // implementation of IController - function claim(address token, uint256 amount, address recipient) external override beforeLocked whenNotPaused { - IVault vault = _validateAndGetVault(token, amount); + function claim(address token, uint256 amount, address recipient) external override beforeLocked whenNotPaused isTokenWhitelisted(token) isValidAmount(amount) { + IVault vault = _getVault(token); vault.withdraw(msg.sender, recipient, amount); } @@ -407,8 +370,7 @@ contract Bootstrap is string calldata operator, address token, uint256 amount - ) external payable override beforeLocked whenNotPaused { - _validateAndGetVault(token, amount); + ) external payable override beforeLocked whenNotPaused isTokenWhitelisted(token) isValidAmount(amount) isValidBech32Address(operator) { // check that operator is registered require(bytes(operators[operator].name).length != 0, "Operator does not exist"); // operator can't be frozen and amount can't be negative @@ -428,8 +390,7 @@ contract Bootstrap is string calldata operator, address token, uint256 amount - ) external payable override beforeLocked whenNotPaused { - _validateAndGetVault(token, amount); + ) external payable override beforeLocked whenNotPaused isTokenWhitelisted(token) isValidAmount(amount) isValidBech32Address(operator) { // check that operator is registered require(bytes(operators[operator].name).length != 0, "Operator does not exist"); // operator can't be frozen and amount can't be negative diff --git a/src/core/ClientChainGateway.sol b/src/core/ClientChainGateway.sol index 4cceb2d6..3ef609cd 100644 --- a/src/core/ClientChainGateway.sol +++ b/src/core/ClientChainGateway.sol @@ -114,41 +114,7 @@ contract ClientChainGateway is // no risk keeping these but they are cheap to clear. delete exocoreSpawnTime; delete offsetDuration; - // // TODO: are these loops even worth it? the maximum refund is 50% of the gas cost. - // // if not, we can remove them. - // // the lines above this set of comments are at least cheaper to clear, - // // and have no utility after initialization. - // for(uint i = 0; i < depositors.length; i++) { - // address depositor = depositors[i]; - // for(uint j = 0; j < whitelistTokens.length; j++) { - // address token = whitelistTokens[j]; - // delete totalDepositAmounts[depositor][token]; - // delete withdrawableAmounts[depositor][token]; - // for(uint k = 0; k < registeredOperators.length; k++) { - // address eth = registeredOperators[k]; - // string memory exo = ethToExocoreAddress[eth]; - // delete delegations[depositor][exo][token]; - // } - // } - // delete isDepositor[depositor]; - // } - // for(uint k = 0; k < registeredOperators.length; k++) { - // address eth = registeredOperators[k]; - // string memory exo = ethToExocoreAddress[eth]; - // delete operators[exo]; - // delete commissionEdited[exo]; - // delete ethToExocoreAddress[eth]; - // for(uint j = 0; j < whitelistTokens.length; j++) { - // address token = whitelistTokens[j]; - // delete delegationsByOperator[exo][token]; - // } - // } - // for(uint j = 0; j < whitelistTokens.length; j++) { - // address token = whitelistTokens[j]; - // delete depositsByToken[token]; - // } - // these should also be cleared - even if the loops are not used - // cheap to clear and potentially large in size. + // previously, we tried clearing the loops but it is too expensive. delete depositors; delete registeredOperators; } @@ -169,29 +135,12 @@ contract ClientChainGateway is _unpause(); } - function addWhitelistToken(address _token) public onlyOwner whenNotPaused { - require(!isWhitelistedToken[_token], "ClientChainGateway: token should not be whitelisted before"); - whitelistTokens.push(_token); - isWhitelistedToken[_token] = true; - emit WhitelistTokenAdded(_token); - - // deploy the corresponding vault if not deployed before - if (address(tokenToVault[_token]) == address(0)) { - _deployVault(_token); - } + function addWhitelistToken(address _token) public override onlyOwner whenNotPaused { + super.addWhitelistToken(_token); } - function removeWhitelistToken(address _token) external isTokenWhitelisted(_token) onlyOwner whenNotPaused { - isWhitelistedToken[_token] = false; - for (uint i = 0; i < whitelistTokens.length; i++) { - if (whitelistTokens[i] == _token) { - whitelistTokens[i] = whitelistTokens[whitelistTokens.length - 1]; - whitelistTokens.pop(); - break; - } - } - - emit WhitelistTokenRemoved(_token); + function removeWhitelistToken(address _token) public override isTokenWhitelisted(_token) onlyOwner whenNotPaused { + super.removeWhitelistToken(_token); } function quote(bytes memory _message) public view returns (uint256 nativeFee) { diff --git a/src/interfaces/IClientChainGateway.sol b/src/interfaces/IClientChainGateway.sol index 9547f828..a8449cd4 100644 --- a/src/interfaces/IClientChainGateway.sol +++ b/src/interfaces/IClientChainGateway.sol @@ -4,10 +4,8 @@ import {IOAppReceiver} from "@layerzero-v2/oapp/contracts/oapp/interfaces/IOAppR import {IOAppCore} from "@layerzero-v2/oapp/contracts/oapp/interfaces/IOAppCore.sol"; import {ILSTRestakingController} from "./ILSTRestakingController.sol"; import {INativeRestakingController} from "../interfaces/INativeRestakingController.sol"; -import {ITokenWhitelister} from "../interfaces/ITokenWhitelister.sol"; interface IClientChainGateway is - ITokenWhitelister, IOAppReceiver, IOAppCore, ILSTRestakingController, diff --git a/src/storage/BootstrapStorage.sol b/src/storage/BootstrapStorage.sol index 8c967817..954b9462 100644 --- a/src/storage/BootstrapStorage.sol +++ b/src/storage/BootstrapStorage.sol @@ -7,12 +7,13 @@ import {IBeacon} from "@openzeppelin-contracts/contracts/proxy/beacon/IBeacon.so import {BeaconProxyBytecode} from "../core/BeaconProxyBytecode.sol"; import {Vault} from "../core/Vault.sol"; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; +import {ITokenWhitelister} from "../interfaces/ITokenWhitelister.sol"; // BootstrapStorage should inherit from GatewayStorage since it exists // prior to ClientChainGateway. ClientChainStorage should inherit from // BootstrapStorage to ensure overlap of positioning between the // members of each contract. -contract BootstrapStorage is GatewayStorage { +contract BootstrapStorage is GatewayStorage, ITokenWhitelister { /* -------------------------------------------------------------------------- */ /* state variables exclusively owned by Bootstrap */ /* -------------------------------------------------------------------------- */ @@ -398,6 +399,29 @@ contract BootstrapStorage is GatewayStorage { uint256[40] private __gap; + modifier isTokenWhitelisted(address token) { + require(isWhitelistedToken[token], "BaseRestakingController: token is not whitelisted"); + _; + } + + modifier isValidAmount(uint256 amount) { + require(amount > 0, "BaseRestakingController: amount should be greater than zero"); + _; + } + + modifier vaultExists(address token) { + require(address(tokenToVault[token]) != address(0), "BaseRestakingController: no vault added for this token"); + _; + } + + modifier isValidBech32Address(string calldata exocoreAddress) { + require( + isValidExocoreAddress(exocoreAddress), + "BaseRestakingController: invalid bech32 encoded Exocore address" + ); + _; + } + constructor(uint32 exocoreChainId_, address vaultBeacon_, address beaconProxyBytecode_) { require(exocoreChainId_ != 0, "BootstrapStorage: exocore chain id should not be empty"); require( @@ -451,4 +475,34 @@ contract BootstrapStorage is GatewayStorage { tokenToVault[underlyingToken] = vault; return vault; } + + // implementation of ITokenWhitelister + function addWhitelistToken(address _token) public virtual override { + require(!isWhitelistedToken[_token], "BootstrapStorage: token should be not whitelisted before"); + whitelistTokens.push(_token); + isWhitelistedToken[_token] = true; + + // deploy the corresponding vault if not deployed before + if (address(tokenToVault[_token]) == address(0)) { + _deployVault(_token); + } + + emit WhitelistTokenAdded(_token); + } + + // implementation of ITokenWhitelister + function removeWhitelistToken(address _token) public virtual override { + isWhitelistedToken[_token] = false; + // the implicit assumption here is that the _token must be included in whitelistTokens + // if isWhitelistedToken[_token] is true + for (uint i = 0; i < whitelistTokens.length; i++) { + if (whitelistTokens[i] == _token) { + whitelistTokens[i] = whitelistTokens[whitelistTokens.length - 1]; + whitelistTokens.pop(); + break; + } + } + + emit WhitelistTokenRemoved(_token); + } } diff --git a/src/storage/ClientChainGatewayStorage.sol b/src/storage/ClientChainGatewayStorage.sol index bef05aa1..f91cef7b 100644 --- a/src/storage/ClientChainGatewayStorage.sol +++ b/src/storage/ClientChainGatewayStorage.sol @@ -50,29 +50,6 @@ contract ClientChainGatewayStorage is BootstrapStorage { error CapsuleNotExist(); - modifier isTokenWhitelisted(address token) { - require(isWhitelistedToken[token], "BaseRestakingController: token is not whitelisted"); - _; - } - - modifier isValidAmount(uint256 amount) { - require(amount > 0, "BaseRestakingController: amount should be greater than zero"); - _; - } - - modifier vaultExists(address token) { - require(address(tokenToVault[token]) != address(0), "BaseRestakingController: no vault added for this token"); - _; - } - - modifier isValidBech32Address(string calldata exocoreAddress) { - require( - isValidExocoreAddress(exocoreAddress), - "BaseRestakingController: invalid bech32 encoded Exocore address" - ); - _; - } - constructor( uint32 exocoreChainId_, address beaconOracleAddress_,