From c298f2aab3b7154acff50ac32abfceb1930f00dd Mon Sep 17 00:00:00 2001 From: John Cairns Date: Wed, 10 Apr 2024 14:48:52 -0500 Subject: [PATCH] 220: cleanup UserWithdrawlManager tests - cleanup UserWithdrawlManager requestId finalization tests - fixes for formatting, leftover from previous change - Dockerfile optimization, move npm and python so they do not depend on code changes --- .devcontainer/devcontainer.json | 2 +- .dockerignore | 5 + .vscode/tasks.json | 4 +- Dockerfile | 20 ++- contracts/L2/ETHx.sol | 14 +- contracts/factory/VaultFactory.sol | 34 ++-- contracts/interfaces/INodeELRewardVault.sol | 2 +- contracts/interfaces/INodeRegistry.sol | 10 +- .../interfaces/IPermissionedNodeRegistry.sol | 17 +- .../IPermissionlessNodeRegistry.sol | 10 +- contracts/interfaces/IPermissionlessPool.sol | 8 +- contracts/interfaces/IPoolUtils.sol | 14 +- .../interfaces/ISDIncentiveController.sol | 2 +- contracts/interfaces/ISDUtilityPool.sol | 18 +- contracts/interfaces/ISocializingPool.sol | 8 +- contracts/interfaces/IStaderOracle.sol | 11 +- contracts/interfaces/IStaderPoolBase.sol | 2 +- .../interfaces/IUserWithdrawalManager.sol | 4 +- .../interfaces/IValidatorWithdrawalVault.sol | 8 +- contracts/interfaces/IVaultProxy.sol | 2 +- contracts/interfaces/IWETH.sol | 8 +- .../interfaces/SDCollateral/IAuction.sol | 2 +- .../interfaces/SDCollateral/ISDCollateral.sol | 13 +- contracts/library/UtilLib.sol | 34 ++-- package-lock.json | 14 +- package.json | 10 +- .../foundry_tests/UserWithdrawalManager.t.sol | 166 +++++++++++------- test/foundry_tests/VaultFactory.t.sol | 2 +- 28 files changed, 232 insertions(+), 212 deletions(-) create mode 100644 .dockerignore diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fd126eac..17e96da1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -26,4 +26,4 @@ "HOLESKY_URL": "${localEnv:HOLESKY_URL}", "ETHERSCAN_API_KEY": "${localEnv:ETHERSCAN_API_KEY}" } -} \ No newline at end of file +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..83a382e6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +Dockerfile +out/ +cache/ +cache_forge/ +artifacts/ diff --git a/.vscode/tasks.json b/.vscode/tasks.json index fc57c623..b77efa25 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -17,7 +17,7 @@ { "label": "install", "type": "shell", - "command": "npm ci", + "command": "npm ci --frozen-lockfile", "options": { "cwd": "${workspaceFolder}" }, @@ -116,4 +116,4 @@ } } ] -} \ No newline at end of file +} diff --git a/Dockerfile b/Dockerfile index 497204da..4b10f030 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,30 @@ FROM ghcr.io/collectivexyz/foundry:latest +RUN python3 -m pip install slither-analyzer --break-system-packages + ARG PROJECT=ethx WORKDIR /workspaces/${PROJECT} -RUN chown -R foundry:foundry . -COPY --chown=foundry:foundry . . + ENV USER=foundry USER foundry ENV PATH=${PATH}:~/.cargo/bin:/usr/local/go/bin -RUN python3 -m pip install slither-analyzer --break-system-packages +RUN chown -R foundry:foundry . -RUN yamlfmt -lint .github/workflows/*.yml +COPY --chown=foundry:foundry package.json . +COPY --chown=foundry:foundry package-lock.json . +COPY --chown=foundry:foundry tsconfig.json . +COPY --chown=foundry:foundry node_modules node_modules RUN npm ci --frozen-lockfile + +COPY --chown=foundry:foundry . . + +RUN yamlfmt -lint .github/workflows/*.yml + +RUN forge install RUN npm run prettier:check # RUN slither . # RUN npm run lint RUN forge test -v -RUN forge geiger --check contracts/*.sol contracts/*/*.sol \ No newline at end of file +RUN forge geiger --check contracts/*.sol contracts/*/*.sol diff --git a/contracts/L2/ETHx.sol b/contracts/L2/ETHx.sol index 04fdb287..431ad29b 100644 --- a/contracts/L2/ETHx.sol +++ b/contracts/L2/ETHx.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol'; +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; /** * @title ETHx token Contract for L2s @@ -13,9 +13,9 @@ import '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol'; contract ETHx is Initializable, ERC20Upgradeable, PausableUpgradeable, AccessControlUpgradeable { error ZeroAddress(); - bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); - bytes32 public constant BURNER_ROLE = keccak256('BURNER_ROLE'); - bytes32 public constant PAUSER_ROLE = keccak256('PAUSER_ROLE'); + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -23,7 +23,7 @@ contract ETHx is Initializable, ERC20Upgradeable, PausableUpgradeable, AccessCon } function initialize(address _admin) external initializer onlyNonZeroAddress(_admin) { - __ERC20_init('ETHx', 'ETHx'); + __ERC20_init("ETHx", "ETHx"); __Pausable_init(); __AccessControl_init(); diff --git a/contracts/factory/VaultFactory.sol b/contracts/factory/VaultFactory.sol index 8fa352ca..a960760d 100644 --- a/contracts/factory/VaultFactory.sol +++ b/contracts/factory/VaultFactory.sol @@ -1,19 +1,19 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../library/UtilLib.sol'; -import '../VaultProxy.sol'; -import '../interfaces/IVaultFactory.sol'; -import '../interfaces/IStaderConfig.sol'; +import "../library/UtilLib.sol"; +import "../VaultProxy.sol"; +import "../interfaces/IVaultFactory.sol"; +import "../interfaces/IStaderConfig.sol"; -import '@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; +import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; contract VaultFactory is IVaultFactory, AccessControlUpgradeable { IStaderConfig public staderConfig; address public vaultProxyImplementation; - bytes32 public constant override NODE_REGISTRY_CONTRACT = keccak256('NODE_REGISTRY_CONTRACT'); + bytes32 public constant override NODE_REGISTRY_CONTRACT = keccak256("NODE_REGISTRY_CONTRACT"); /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -45,12 +45,10 @@ contract VaultFactory is IVaultFactory, AccessControlUpgradeable { return withdrawVaultAddress; } - function deployNodeELRewardVault(uint8 _poolId, uint256 _operatorId) - external - override - onlyRole(NODE_REGISTRY_CONTRACT) - returns (address) - { + function deployNodeELRewardVault( + uint8 _poolId, + uint256 _operatorId + ) external override onlyRole(NODE_REGISTRY_CONTRACT) returns (address) { bytes32 salt = sha256(abi.encode(_poolId, _operatorId)); address nodeELRewardVaultAddress = ClonesUpgradeable.cloneDeterministic(vaultProxyImplementation, salt); VaultProxy(payable(nodeELRewardVaultAddress)).initialise(false, _poolId, _operatorId, address(staderConfig)); @@ -68,12 +66,10 @@ contract VaultFactory is IVaultFactory, AccessControlUpgradeable { return ClonesUpgradeable.predictDeterministicAddress(vaultProxyImplementation, salt); } - function computeNodeELRewardVaultAddress(uint8 _poolId, uint256 _operatorId) - external - view - override - returns (address) - { + function computeNodeELRewardVaultAddress( + uint8 _poolId, + uint256 _operatorId + ) external view override returns (address) { bytes32 salt = sha256(abi.encode(_poolId, _operatorId)); return ClonesUpgradeable.predictDeterministicAddress(vaultProxyImplementation, salt); } diff --git a/contracts/interfaces/INodeELRewardVault.sol b/contracts/interfaces/INodeELRewardVault.sol index ba66d8fd..d466591d 100644 --- a/contracts/interfaces/INodeELRewardVault.sol +++ b/contracts/interfaces/INodeELRewardVault.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import './IStaderConfig.sol'; +import "./IStaderConfig.sol"; interface INodeELRewardVault { // errors diff --git a/contracts/interfaces/INodeRegistry.sol b/contracts/interfaces/INodeRegistry.sol index b8ed7ed5..06abd451 100644 --- a/contracts/interfaces/INodeRegistry.sol +++ b/contracts/interfaces/INodeRegistry.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../library/ValidatorStatus.sol'; +import "../library/ValidatorStatus.sol"; struct Validator { ValidatorStatus status; // status of validator @@ -68,7 +68,9 @@ interface INodeRegistry { ) external; // return validator struct for a validator Id - function validatorRegistry(uint256) + function validatorRegistry( + uint256 + ) external view returns ( @@ -83,7 +85,9 @@ interface INodeRegistry { ); // returns the operator struct given operator Id - function operatorStructById(uint256) + function operatorStructById( + uint256 + ) external view returns ( diff --git a/contracts/interfaces/IPermissionedNodeRegistry.sol b/contracts/interfaces/IPermissionedNodeRegistry.sol index 28163235..48ba9195 100644 --- a/contracts/interfaces/IPermissionedNodeRegistry.sol +++ b/contracts/interfaces/IPermissionedNodeRegistry.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../library/ValidatorStatus.sol'; -import './INodeRegistry.sol'; +import "../library/ValidatorStatus.sol"; +import "./INodeRegistry.sol"; interface IPermissionedNodeRegistry { // Errors @@ -38,9 +38,10 @@ interface IPermissionedNodeRegistry { function whitelistPermissionedNOs(address[] calldata _permissionedNOs) external; - function onboardNodeOperator(string calldata _operatorName, address payable _operatorRewardAddress) - external - returns (address mevFeeRecipientAddress); + function onboardNodeOperator( + string calldata _operatorName, + address payable _operatorRewardAddress + ) external returns (address mevFeeRecipientAddress); function addValidatorKeys( bytes[] calldata _pubkey, @@ -48,9 +49,9 @@ interface IPermissionedNodeRegistry { bytes[] calldata _depositSignature ) external; - function allocateValidatorsAndUpdateOperatorId(uint256 _numValidators) - external - returns (uint256[] memory selectedOperatorCapacity); + function allocateValidatorsAndUpdateOperatorId( + uint256 _numValidators + ) external returns (uint256[] memory selectedOperatorCapacity); function activateNodeOperator(uint256 _operatorId) external; diff --git a/contracts/interfaces/IPermissionlessNodeRegistry.sol b/contracts/interfaces/IPermissionlessNodeRegistry.sol index c8b22101..48289cbb 100644 --- a/contracts/interfaces/IPermissionlessNodeRegistry.sol +++ b/contracts/interfaces/IPermissionlessNodeRegistry.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../library/ValidatorStatus.sol'; -import './INodeRegistry.sol'; +import "../library/ValidatorStatus.sol"; +import "./INodeRegistry.sol"; interface IPermissionlessNodeRegistry { // Errors @@ -79,9 +79,9 @@ interface IPermissionlessNodeRegistry { function updateOperatorName(string calldata _operatorName) external; - function changeSocializingPoolState(bool _optInForSocializingPool) - external - returns (address mevFeeRecipientAddress); + function changeSocializingPoolState( + bool _optInForSocializingPool + ) external returns (address mevFeeRecipientAddress); function pause() external; diff --git a/contracts/interfaces/IPermissionlessPool.sol b/contracts/interfaces/IPermissionlessPool.sol index 7d5f45a8..0ce1719b 100644 --- a/contracts/interfaces/IPermissionlessPool.sol +++ b/contracts/interfaces/IPermissionlessPool.sol @@ -11,8 +11,8 @@ interface IPermissionlessPool { function receiveRemainingCollateralETH() external payable; - function getAllSocializingPoolOptOutOperators(uint256 _pageNumber, uint256 _pageSize) - external - view - returns (address[] memory); + function getAllSocializingPoolOptOutOperators( + uint256 _pageNumber, + uint256 _pageSize + ) external view returns (address[] memory); } diff --git a/contracts/interfaces/IPoolUtils.sol b/contracts/interfaces/IPoolUtils.sol index 7cb0a85a..4083d06b 100644 --- a/contracts/interfaces/IPoolUtils.sol +++ b/contracts/interfaces/IPoolUtils.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import './INodeRegistry.sol'; +import "./INodeRegistry.sol"; // Interface for the PoolUtils contract interface IPoolUtils { @@ -85,12 +85,8 @@ interface IPoolUtils { bytes calldata _depositSignature ) external; - function calculateRewardShare(uint8 _poolId, uint256 _totalRewards) - external - view - returns ( - uint256 userShare, - uint256 operatorShare, - uint256 protocolShare - ); + function calculateRewardShare( + uint8 _poolId, + uint256 _totalRewards + ) external view returns (uint256 userShare, uint256 operatorShare, uint256 protocolShare); } diff --git a/contracts/interfaces/ISDIncentiveController.sol b/contracts/interfaces/ISDIncentiveController.sol index ba07cc3a..b2b82118 100644 --- a/contracts/interfaces/ISDIncentiveController.sol +++ b/contracts/interfaces/ISDIncentiveController.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import './IStaderConfig.sol'; +import "./IStaderConfig.sol"; interface ISDIncentiveController { //errors diff --git a/contracts/interfaces/ISDUtilityPool.sol b/contracts/interfaces/ISDUtilityPool.sol index 667559e6..ef8b7f87 100644 --- a/contracts/interfaces/ISDUtilityPool.sol +++ b/contracts/interfaces/ISDUtilityPool.sol @@ -113,11 +113,7 @@ interface ISDUtilityPool { function utilize(uint256 utilizeAmount) external; - function utilizeWhileAddingKeys( - address operator, - uint256 utilizeAmount, - uint256 nonTerminalKeyCount - ) external; + function utilizeWhileAddingKeys(address operator, uint256 utilizeAmount, uint256 nonTerminalKeyCount) external; function repayFullAmount() external returns (uint256, uint256); @@ -200,16 +196,12 @@ interface ISDUtilityPool { function getOperatorLiquidation(address) external view returns (OperatorLiquidation memory); - function delegatorWithdrawRequests(uint256) + function delegatorWithdrawRequests( + uint256 + ) external view - returns ( - address owner, - uint256 amountOfCToken, - uint256 sdExpected, - uint256 sdFinalized, - uint256 requestBlock - ); + returns (address owner, uint256 amountOfCToken, uint256 sdExpected, uint256 sdFinalized, uint256 requestBlock); function requestIdsByDelegatorAddress(address, uint256) external view returns (uint256); diff --git a/contracts/interfaces/ISocializingPool.sol b/contracts/interfaces/ISocializingPool.sol index feec8fe6..502e8156 100644 --- a/contracts/interfaces/ISocializingPool.sol +++ b/contracts/interfaces/ISocializingPool.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; -import './IStaderConfig.sol'; +import "./IStaderConfig.sol"; /// @title RewardsData /// @notice This struct holds rewards merkleRoot and rewards split @@ -100,11 +100,7 @@ interface ISocializingPool { function getRewardDetails() external view - returns ( - uint256 currentIndex, - uint256 currentStartBlock, - uint256 currentEndBlock - ); + returns (uint256 currentIndex, uint256 currentStartBlock, uint256 currentEndBlock); function getRewardCycleDetails(uint256 _index) external view returns (uint256 _startBlock, uint256 _endBlock); } diff --git a/contracts/interfaces/IStaderOracle.sol b/contracts/interfaces/IStaderOracle.sol index 8c03b32a..deef3768 100644 --- a/contracts/interfaces/IStaderOracle.sol +++ b/contracts/interfaces/IStaderOracle.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../library/ValidatorStatus.sol'; +import "../library/ValidatorStatus.sol"; -import './ISocializingPool.sol'; -import './IStaderConfig.sol'; +import "./ISocializingPool.sol"; +import "./IStaderConfig.sol"; struct SDPriceData { uint256 reportingBlockNumber; @@ -240,8 +240,9 @@ interface IStaderOracle { * and if the submission count reaches the required threshold, it updates the markValidatorReadyToDeposit (NodeRegistry). * @param _validatorVerificationDetail validator verification data, containing valid pubkeys, front run and invalid signature */ - function submitValidatorVerificationDetail(ValidatorVerificationDetail calldata _validatorVerificationDetail) - external; + function submitValidatorVerificationDetail( + ValidatorVerificationDetail calldata _validatorVerificationDetail + ) external; /** * @notice store the missed attestation penalty strike on validator diff --git a/contracts/interfaces/IStaderPoolBase.sol b/contracts/interfaces/IStaderPoolBase.sol index 8320b72e..e9bc5ea2 100644 --- a/contracts/interfaces/IStaderPoolBase.sol +++ b/contracts/interfaces/IStaderPoolBase.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import './INodeRegistry.sol'; +import "./INodeRegistry.sol"; interface IStaderPoolBase { // Errors diff --git a/contracts/interfaces/IUserWithdrawalManager.sol b/contracts/interfaces/IUserWithdrawalManager.sol index 2520252c..a78758ee 100644 --- a/contracts/interfaces/IUserWithdrawalManager.sol +++ b/contracts/interfaces/IUserWithdrawalManager.sol @@ -55,7 +55,9 @@ interface IUserWithdrawalManager { function maxNonRedeemedUserRequestCount() external view returns (uint256); - function userWithdrawRequests(uint256) + function userWithdrawRequests( + uint256 + ) external view returns ( diff --git a/contracts/interfaces/IValidatorWithdrawalVault.sol b/contracts/interfaces/IValidatorWithdrawalVault.sol index 0bd9a1ff..6d0d74b9 100644 --- a/contracts/interfaces/IValidatorWithdrawalVault.sol +++ b/contracts/interfaces/IValidatorWithdrawalVault.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import './IStaderConfig.sol'; +import "./IStaderConfig.sol"; interface IValidatorWithdrawalVault { // Errors @@ -26,9 +26,5 @@ interface IValidatorWithdrawalVault { function calculateValidatorWithdrawalShare() external view - returns ( - uint256 _userShare, - uint256 _operatorShare, - uint256 _protocolShare - ); + returns (uint256 _userShare, uint256 _operatorShare, uint256 _protocolShare); } diff --git a/contracts/interfaces/IVaultProxy.sol b/contracts/interfaces/IVaultProxy.sol index af552779..37a12ec8 100644 --- a/contracts/interfaces/IVaultProxy.sol +++ b/contracts/interfaces/IVaultProxy.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import './IStaderConfig.sol'; +import "./IStaderConfig.sol"; interface IVaultProxy { error CallerNotOwner(); diff --git a/contracts/interfaces/IWETH.sol b/contracts/interfaces/IWETH.sol index 48bf7dbe..bc4eefbc 100644 --- a/contracts/interfaces/IWETH.sol +++ b/contracts/interfaces/IWETH.sol @@ -1,16 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IWETH is IERC20 { function deposit() external payable; function withdraw(uint256) external; - function transferFrom( - address src, - address dst, - uint256 wad - ) external returns (bool); + function transferFrom(address src, address dst, uint256 wad) external returns (bool); } diff --git a/contracts/interfaces/SDCollateral/IAuction.sol b/contracts/interfaces/SDCollateral/IAuction.sol index ae1b666a..e6b63cb9 100644 --- a/contracts/interfaces/SDCollateral/IAuction.sol +++ b/contracts/interfaces/SDCollateral/IAuction.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../IStaderConfig.sol'; +import "../IStaderConfig.sol"; interface IAuction { // errors diff --git a/contracts/interfaces/SDCollateral/ISDCollateral.sol b/contracts/interfaces/SDCollateral/ISDCollateral.sol index ada3eff9..f45ef289 100644 --- a/contracts/interfaces/SDCollateral/ISDCollateral.sol +++ b/contracts/interfaces/SDCollateral/ISDCollateral.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../IStaderConfig.sol'; +import "../IStaderConfig.sol"; interface ISDCollateral { struct PoolThresholdInfo { @@ -91,12 +91,7 @@ interface ISDCollateral { function convertETHToSD(uint256 _ethAmount) external view returns (uint256); - function getOperatorInfo(address _operator) - external - view - returns ( - uint8 _poolId, - uint256 _operatorId, - uint256 _validatorCount - ); + function getOperatorInfo( + address _operator + ) external view returns (uint8 _poolId, uint256 _operatorId, uint256 _validatorCount); } diff --git a/contracts/library/UtilLib.sol b/contracts/library/UtilLib.sol index 3d0f489a..c0695ebe 100644 --- a/contracts/library/UtilLib.sol +++ b/contracts/library/UtilLib.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import '../interfaces/IStaderConfig.sol'; -import '../interfaces/INodeRegistry.sol'; -import '../interfaces/IPoolUtils.sol'; -import '../interfaces/IVaultProxy.sol'; +import "../interfaces/IStaderConfig.sol"; +import "../interfaces/INodeRegistry.sol"; +import "../interfaces/IPoolUtils.sol"; +import "../interfaces/IVaultProxy.sol"; library UtilLib { error ZeroAddress(); @@ -36,11 +36,7 @@ library UtilLib { } //checks if caller is a stader contract address - function onlyStaderContract( - address _addr, - IStaderConfig _staderConfig, - bytes32 _contractName - ) internal view { + function onlyStaderContract(address _addr, IStaderConfig _staderConfig, bytes32 _contractName) internal view { if (!_staderConfig.onlyStaderContract(_addr, _contractName)) { revert CallerNotStaderContract(); } @@ -115,11 +111,10 @@ library UtilLib { return operatorAddress; } - function getOperatorRewardAddress(address _operator, IStaderConfig _staderConfig) - internal - view - returns (address payable) - { + function getOperatorRewardAddress( + address _operator, + IStaderConfig _staderConfig + ) internal view returns (address payable) { uint8 poolId = IPoolUtils(_staderConfig.getPoolUtils()).getOperatorPoolId(_operator); address nodeRegistry = IPoolUtils(_staderConfig.getPoolUtils()).getNodeRegistry(poolId); uint256 operatorId = INodeRegistry(nodeRegistry).operatorIDByAddress(_operator); @@ -140,11 +135,10 @@ library UtilLib { return sha256(abi.encodePacked(_pubkey, bytes16(0))); } - function getValidatorSettleStatus(bytes calldata _pubkey, IStaderConfig _staderConfig) - internal - view - returns (bool) - { + function getValidatorSettleStatus( + bytes calldata _pubkey, + IStaderConfig _staderConfig + ) internal view returns (bool) { uint8 poolId = IPoolUtils(_staderConfig.getPoolUtils()).getValidatorPoolId(_pubkey); address nodeRegistry = IPoolUtils(_staderConfig.getPoolUtils()).getNodeRegistry(poolId); uint256 validatorId = INodeRegistry(nodeRegistry).validatorIdByPubkey(_pubkey); @@ -165,7 +159,7 @@ library UtilLib { } function sendValue(address _receiver, uint256 _amount) internal { - (bool success, ) = payable(_receiver).call{value: _amount}(''); + (bool success, ) = payable(_receiver).call{ value: _amount }(""); if (!success) { revert TransferFailed(); } diff --git a/package-lock.json b/package-lock.json index ae7ab9eb..9cd15439 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "hardhat": "^2.9.9", "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", - "solhint": "4.5.2", + "solhint": "4.5.4", "typechain": "^8.1.1" } }, @@ -10853,9 +10853,9 @@ } }, "node_modules/solhint": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.2.tgz", - "integrity": "sha512-o7MNYS5QPgE6l+PTGOTAUtCzo0ZLnffQsv586hntSHBe2JbSDfkoxfhAOcjZjN4OesTgaX4UEEjCjH9y/4BP5w==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.4.tgz", + "integrity": "sha512-Cu1XiJXub2q1eCr9kkJ9VPv1sGcmj3V7Zb76B0CoezDOB9bu3DxKIFFH7ggCl9fWpEPD6xBmRLfZrYijkVmujQ==", "dev": true, "dependencies": { "@solidity-parser/parser": "^0.18.0", @@ -21999,9 +21999,9 @@ } }, "solhint": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.2.tgz", - "integrity": "sha512-o7MNYS5QPgE6l+PTGOTAUtCzo0ZLnffQsv586hntSHBe2JbSDfkoxfhAOcjZjN4OesTgaX4UEEjCjH9y/4BP5w==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-4.5.4.tgz", + "integrity": "sha512-Cu1XiJXub2q1eCr9kkJ9VPv1sGcmj3V7Zb76B0CoezDOB9bu3DxKIFFH7ggCl9fWpEPD6xBmRLfZrYijkVmujQ==", "dev": true, "requires": { "@solidity-parser/parser": "^0.18.0", diff --git a/package.json b/package.json index d95f7b5a..1635d3c6 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "prettier:check": "prettier --check \"(contracts|test|script)/**.sol\"", - "prettier:fix": "prettier --write \"(contracts|test|script)/**.sol\"", + "test": "forge test -v", + "prettier:check": "prettier --check \"(contracts|test|script)/**/*.sol\"", + "prettier:fix": "prettier --write \"(contracts|test|script)/**/*.sol\"", "lint": "solhint -w 3 'contracts/**/*.sol' 'test/**/*.sol' 'script/**/*.sol'" }, "repository": { @@ -32,7 +32,7 @@ "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "typechain": "^8.1.1", - "solhint": "4.5.2" + "solhint": "^4.5.2" }, "dependencies": { "@chainlink/contracts": "^0.5.1", @@ -45,4 +45,4 @@ "get-random-values": "^2.0.0", "ssv-keys": "github:bloxapp/ssv-keys" } -} \ No newline at end of file +} diff --git a/test/foundry_tests/UserWithdrawalManager.t.sol b/test/foundry_tests/UserWithdrawalManager.t.sol index 9e0fecda..9d749f62 100644 --- a/test/foundry_tests/UserWithdrawalManager.t.sol +++ b/test/foundry_tests/UserWithdrawalManager.t.sol @@ -1,30 +1,37 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.16; -import "../../contracts/library/UtilLib.sol"; +import { Test } from "forge-std/Test.sol"; -import "../../contracts/ETHx.sol"; -import "../../contracts/StaderConfig.sol"; -import "../../contracts/UserWithdrawalManager.sol"; +import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "../mocks/StaderOracleMock.sol"; -import "../mocks/StakePoolManagerMock.sol"; +import { UtilLib } from "../../contracts/library/UtilLib.sol"; -import "forge-std/Test.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import { IUserWithdrawalManager } from "../../contracts/interfaces/IUserWithdrawalManager.sol"; +import { IStaderStakePoolManager } from "../../contracts/interfaces/IStaderStakePoolManager.sol"; +import { IStaderOracle } from "../../contracts/interfaces/IStaderOracle.sol"; + +import { ETHx } from "../../contracts/ETHx.sol"; +import { StaderConfig } from "../../contracts/StaderConfig.sol"; +import { UserWithdrawalManager } from "../../contracts/UserWithdrawalManager.sol"; + +import { StaderOracleMock } from "../mocks/StaderOracleMock.sol"; +import { StakePoolManagerMock } from "../mocks/StakePoolManagerMock.sol"; contract UserWithdrawalManagerTest is Test { - address staderAdmin; - address staderManager; - address operator; + event FinalizedWithdrawRequest(uint256 requestId); - ETHx ethX; - StaderConfig staderConfig; - UserWithdrawalManager userWithdrawalManager; + address private staderAdmin; + address private staderManager; + address private operator; - StaderOracleMock staderOracle; - StakePoolManagerMock staderStakePoolManager; + ETHx private ethX; + StaderConfig private staderConfig; + UserWithdrawalManager private userWithdrawalManager; + + StaderOracleMock private staderOracle; + StakePoolManagerMock private staderStakePoolManager; function setUp() public { vm.clearMockedCalls(); @@ -117,28 +124,22 @@ contract UserWithdrawalManagerTest is Test { vm.startPrank(staderAdmin); userWithdrawalManager.updateStaderConfig(newStaderConfig); assertEq(address(userWithdrawalManager.staderConfig()), newStaderConfig); + vm.stopPrank(); } - function testFail_updateStaderConfig(uint64 _staderConfigSeed) public { - vm.assume(_staderConfigSeed > 0); + function test_updateStaderConfigRequiresAdmin() public { + uint _staderConfigSeed = 1001; address newStaderConfig = vm.addr(_staderConfigSeed); + vm.expectRevert( + "AccessControl: account 0x7fa9385be102ac3eac297483dd6233d62b3e1496 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" + ); userWithdrawalManager.updateStaderConfig(newStaderConfig); - assertEq(address(userWithdrawalManager.staderConfig()), newStaderConfig); } - function test_requestWithdraw(uint64 randomPrivateKey, uint64 randomPrivateKey2) public { - vm.assume( - randomPrivateKey > 0 && - vm.addr(randomPrivateKey) != address(userWithdrawalManager) && - vm.addr(randomPrivateKey) != address(staderStakePoolManager) - ); - vm.assume( - randomPrivateKey2 > 0 && - vm.addr(randomPrivateKey) != address(userWithdrawalManager) && - vm.addr(randomPrivateKey) != address(staderStakePoolManager) - ); - address ethXHolder = vm.addr(randomPrivateKey); - address owner = vm.addr(randomPrivateKey2); + function test_requestWithdraw() public { + address ethXHolder = vm.addr(1001); + address owner = vm.addr(1002); + vm.prank(staderManager); userWithdrawalManager.pause(); vm.expectRevert(); @@ -167,44 +168,15 @@ contract UserWithdrawalManagerTest is Test { userWithdrawalManager.requestWithdraw(100 ether, owner); } - function test_finalizeUserWithdrawalRequest(uint64 randomPrivateKey, uint64 randomPrivateKey2) public { - vm.assume(randomPrivateKey != randomPrivateKey2); - vm.assume( - randomPrivateKey > 0 && - vm.addr(randomPrivateKey) != address(userWithdrawalManager) && - vm.addr(randomPrivateKey) != address(staderStakePoolManager) - ); - vm.assume( - randomPrivateKey2 > 0 && - vm.addr(randomPrivateKey) != address(userWithdrawalManager) && - vm.addr(randomPrivateKey) != address(staderStakePoolManager) - ); - address ethXHolder = vm.addr(randomPrivateKey); - address owner = vm.addr(randomPrivateKey2); - - vm.prank(staderManager); - userWithdrawalManager.pause(); - vm.expectRevert(); - userWithdrawalManager.finalizeUserWithdrawalRequest(); - vm.prank(staderAdmin); - userWithdrawalManager.unpause(); - vm.mockCall(address(staderOracle), abi.encodeWithSelector(IStaderOracle.safeMode.selector), abi.encode(true)); - vm.mockCall( - address(staderStakePoolManager), - abi.encodeWithSelector(IStaderStakePoolManager.isVaultHealthy.selector), - abi.encode(false) - ); - - vm.expectRevert(IUserWithdrawalManager.UnsupportedOperationInSafeMode.selector); - userWithdrawalManager.finalizeUserWithdrawalRequest(); + function test_finalizeUserWithdrawalRequest() public { vm.mockCall(address(staderOracle), abi.encodeWithSelector(IStaderOracle.safeMode.selector), abi.encode(false)); - vm.expectRevert(IUserWithdrawalManager.ProtocolNotHealthy.selector); - userWithdrawalManager.finalizeUserWithdrawalRequest(); vm.mockCall( address(staderStakePoolManager), abi.encodeWithSelector(IStaderStakePoolManager.isVaultHealthy.selector), abi.encode(true) ); + address ethXHolder = vm.addr(1001); + address owner = vm.addr(1002); vm.prank(address(staderStakePoolManager)); ethX.mint(ethXHolder, 100 ether); assertEq(ethX.balanceOf(ethXHolder), 100 ether); @@ -250,6 +222,70 @@ contract UserWithdrawalManagerTest is Test { assertEq(userWithdrawalManager.ethRequestedForWithdraw(), 0); assertEq(address(userWithdrawalManager).balance, 40 ether); assertEq(userWithdrawalManager.nextRequestIdToFinalize(), 6); + vm.stopPrank(); + } + + function test_finalizeUserWithdrawalRequestEmitFinalizedWithdrawRequest() public { + vm.mockCall(address(staderOracle), abi.encodeWithSelector(IStaderOracle.safeMode.selector), abi.encode(false)); + vm.mockCall( + address(staderStakePoolManager), + abi.encodeWithSelector(IStaderStakePoolManager.isVaultHealthy.selector), + abi.encode(true) + ); + address ethXHolder = vm.addr(1001); + address owner = vm.addr(1002); + vm.prank(address(staderStakePoolManager)); + ethX.mint(ethXHolder, 100 ether); + assertEq(ethX.balanceOf(ethXHolder), 100 ether); + vm.startPrank(ethXHolder); + ethX.approve(address(userWithdrawalManager), type(uint256).max); + userWithdrawalManager.requestWithdraw(10 ether, owner); + userWithdrawalManager.requestWithdraw(10 ether, ethXHolder); + + uint nextRequestId = userWithdrawalManager.nextRequestId(); + assertEq(nextRequestId, 3); + assertEq(userWithdrawalManager.nextRequestIdToFinalize(), 1); + + vm.deal(address(staderStakePoolManager), 20 ether); + vm.roll(block.number + 600); + vm.expectEmit(); + emit FinalizedWithdrawRequest(3); + userWithdrawalManager.finalizeUserWithdrawalRequest(); + vm.stopPrank(); + } + + function test_finalizeUserWithdrawalRequestRevertIfPaused() public { + vm.prank(staderManager); + userWithdrawalManager.pause(); + vm.expectRevert(); + userWithdrawalManager.finalizeUserWithdrawalRequest(); + } + + function test_finalizeUserWithdrawalRequestRevertInSafeMode() public { + vm.prank(staderAdmin); + //userWithdrawalManager.unpause(); + vm.mockCall(address(staderOracle), abi.encodeWithSelector(IStaderOracle.safeMode.selector), abi.encode(true)); + vm.mockCall( + address(staderStakePoolManager), + abi.encodeWithSelector(IStaderStakePoolManager.isVaultHealthy.selector), + abi.encode(false) + ); + + vm.expectRevert(IUserWithdrawalManager.UnsupportedOperationInSafeMode.selector); + userWithdrawalManager.finalizeUserWithdrawalRequest(); + } + + function test_finalizeUserWithdrawalRequest_revertProtocolNotHealthy() public { + vm.prank(staderAdmin); + //userWithdrawalManager.unpause(); + vm.mockCall( + address(staderStakePoolManager), + abi.encodeWithSelector(IStaderStakePoolManager.isVaultHealthy.selector), + abi.encode(false) + ); + vm.mockCall(address(staderOracle), abi.encodeWithSelector(IStaderOracle.safeMode.selector), abi.encode(false)); + vm.expectRevert(IUserWithdrawalManager.ProtocolNotHealthy.selector); + userWithdrawalManager.finalizeUserWithdrawalRequest(); } function test_claim(uint64 randomPrivateKey, uint64 randomPrivateKey2) public { diff --git a/test/foundry_tests/VaultFactory.t.sol b/test/foundry_tests/VaultFactory.t.sol index 8434fccc..5ed909de 100644 --- a/test/foundry_tests/VaultFactory.t.sol +++ b/test/foundry_tests/VaultFactory.t.sol @@ -26,7 +26,7 @@ contract VaultFactoryTest is Test { staderAdmin = vm.addr(100); address ethDepositAddr = vm.addr(102); address operator = address(500); - + ProxyAdmin proxyAdmin = new ProxyAdmin(); StaderConfig configImpl = new StaderConfig();