Skip to content

Commit

Permalink
refactor: implement token whitelisting in storage
Browse files Browse the repository at this point in the history
This way, we avoid repeating the token whitelisting code in `Bootstrap`
and `ClientChainGateway`.
  • Loading branch information
MaxMustermann2 committed Jun 3, 2024
1 parent 60c949e commit 925bf36
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 133 deletions.
63 changes: 12 additions & 51 deletions src/core/Bootstrap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -29,7 +28,6 @@ contract Bootstrap is
Initializable,
PausableUpgradeable,
OwnableUpgradeable,
ITokenWhitelister,
ILSTRestakingController,
IOperatorRegistry,
BootstrapLzReceiver
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]) {
Expand All @@ -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");
Expand All @@ -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);
}

Expand All @@ -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
Expand All @@ -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
Expand Down
61 changes: 5 additions & 56 deletions src/core/ClientChainGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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) {
Expand Down
2 changes: 0 additions & 2 deletions src/interfaces/IClientChainGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
56 changes: 55 additions & 1 deletion src/storage/BootstrapStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
}
}
23 changes: 0 additions & 23 deletions src/storage/ClientChainGatewayStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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_,
Expand Down

0 comments on commit 925bf36

Please sign in to comment.