From 1d2e01105c36f8287bdc15be27c482a4eaf48977 Mon Sep 17 00:00:00 2001 From: jtfirek Date: Wed, 28 Aug 2024 11:53:19 -0500 Subject: [PATCH 1/3] refactor: liquifier --- src/Liquifier.sol | 135 ++--------------------------- test/Liquifier.t.sol | 198 ------------------------------------------- 2 files changed, 6 insertions(+), 327 deletions(-) diff --git a/src/Liquifier.sol b/src/Liquifier.sol index 816d4c33..3a7f4c90 100644 --- a/src/Liquifier.sol +++ b/src/Liquifier.sol @@ -61,12 +61,12 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab uint128 public DEPRECATED_accumulatedFee; mapping(address => TokenInfo) public tokenInfos; - mapping(bytes32 => bool) public isRegisteredQueuedWithdrawals; + mapping(bytes32 => bool) public DEPRECATED_isRegisteredQueuedWithdrawals; mapping(address => bool) public DEPRECATED_admins; address public treasury; ILiquidityPool public liquidityPool; - IStrategyManager public eigenLayerStrategyManager; + IStrategyManager public DEPRECATED_eigenLayerStrategyManager; ILidoWithdrawalQueue public lidoWithdrawalQueue; ICurvePool public cbEth_Eth_Pool; @@ -77,7 +77,7 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab IwBETH public wbEth; ILido public lido; - IDelegationManager public eigenLayerDelegationManager; + IDelegationManager public DEPRECATED_eigenLayerDelegationManager; IPancackeV3SwapRouter pancakeRouter; @@ -96,19 +96,14 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab bytes32 public constant LIQUIFIER_ADMIN_ROLE = keccak256("LIQUIFIER_ADMIN_ROLE"); event Liquified(address _user, uint256 _toEEthAmount, address _fromToken, bool _isRestaked); - event RegisteredQueuedWithdrawal(bytes32 _withdrawalRoot, IStrategyManager.DeprecatedStruct_QueuedWithdrawal _queuedWithdrawal); - event RegisteredQueuedWithdrawal_V2(bytes32 _withdrawalRoot, IDelegationManager.Withdrawal _queuedWithdrawal); - event CompletedQueuedWithdrawal(bytes32 _withdrawalRoot); event QueuedStEthWithdrawals(uint256[] _reqIds); event CompletedStEthQueuedWithdrawals(uint256[] _reqIds); - error StrategyShareNotEnough(); error NotSupportedToken(); error EthTransferFailed(); error NotEnoughBalance(); error IncorrectAmount(); error AlreadyRegistered(); - error NotRegistered(); error WrongOutput(); error IncorrectCaller(); error IncorrectRole(); @@ -130,7 +125,7 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab treasury = _treasury; liquidityPool = ILiquidityPool(_liquidityPool); lidoWithdrawalQueue = ILidoWithdrawalQueue(_lidoWithdrawalQueue); - eigenLayerStrategyManager = IEigenLayerStrategyManager(_eigenLayerStrategyManager); + DEPRECATED_eigenLayerStrategyManager = IEigenLayerStrategyManager(_eigenLayerStrategyManager); lido = ILido(_stEth); cbEth = IcbETH(_cbEth); @@ -152,30 +147,6 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab rateLimiter = BucketRateLimiter(_rateLimiter); } - receive() external payable {} - - /// the users mint eETH given the queued withdrawal for their LRT with withdrawer == address(this) - /// @param _queuedWithdrawal The QueuedWithdrawal to be used for the deposit. This is the proof that the user has the re-staked ETH and requested the withdrawals setting the Liquifier contract as the withdrawer. - /// @param _referral The referral address - /// @return mintedAmount the amount of eETH minted to the caller (= msg.sender) - function depositWithQueuedWithdrawal(IDelegationManager.Withdrawal calldata _queuedWithdrawal, address _referral) external whenNotPaused nonReentrant returns (uint256) { - bytes32 withdrawalRoot = verifyQueuedWithdrawal(msg.sender, _queuedWithdrawal); - - /// register it to prevent duplicate deposits with the same queued withdrawal - isRegisteredQueuedWithdrawals[withdrawalRoot] = true; - emit RegisteredQueuedWithdrawal_V2(withdrawalRoot, _queuedWithdrawal); - - /// queue the strategy share for withdrawal - uint256 amount = _enqueueForWithdrawal(_queuedWithdrawal.strategies, _queuedWithdrawal.shares); - - /// mint eETH to the user - uint256 eEthShare = liquidityPool.depositToRecipient(msg.sender, amount, _referral); - - _consumeRate(address(eigenLayerStrategyManager), amount, eEthShare); - - return eEthShare; - } - /// Deposit Liquid Staking Token such as stETH and Mint eETH /// @param _token The address of the token to deposit /// @param _amount The amount of the token to deposit @@ -219,27 +190,6 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab return depositWithERC20(_token, _amount, _referral); } - /// @notice Used to complete the specified `queuedWithdrawals`. The function caller must match `queuedWithdrawals[...].withdrawer` - /// @param _queuedWithdrawals The QueuedWithdrawals to complete. - /// @param _tokens Array of tokens for each QueuedWithdrawal. See `completeQueuedWithdrawal` for the usage of a single array. - /// @param _middlewareTimesIndexes One index to reference per QueuedWithdrawal. See `completeQueuedWithdrawal` for the usage of a single index. - /// @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw` - function completeQueuedWithdrawals(IDelegationManager.Withdrawal[] calldata _queuedWithdrawals, IERC20[][] calldata _tokens, uint256[] calldata _middlewareTimesIndexes) external { - if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); - - uint256 num = _queuedWithdrawals.length; - bool[] memory receiveAsTokens = new bool[](num); - for (uint256 i = 0; i < num; i++) { - _completeWithdrawals(_queuedWithdrawals[i]); - - /// so that the shares withdrawn from the specified strategies are sent to the caller - receiveAsTokens[i] = true; - } - - /// it will update the erc20 balances of this contract - eigenLayerDelegationManager.completeQueuedWithdrawals(_queuedWithdrawals, _tokens, _middlewareTimesIndexes, receiveAsTokens); - } - /// Initiate the process for redemption of stETH function stEthRequestWithdrawal() external returns (uint256[] memory) { if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); @@ -367,22 +317,6 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab if (currentBalance < _minOutputAmount + beforeBalance) revert WrongOutput(); } - function swapCbEthToEth(uint256 _amount, uint256 _minOutputAmount) external returns (uint256) { - if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); - if (_amount > cbEth.balanceOf(address(this))) revert NotEnoughBalance(); - - cbEth.approve(address(cbEth_Eth_Pool), _amount); - return cbEth_Eth_Pool.exchange_underlying(1, 0, _amount, _minOutputAmount); - } - - function swapWbEthToEth(uint256 _amount, uint256 _minOutputAmount) external returns (uint256) { - if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); - if (_amount > wbEth.balanceOf(address(this))) revert NotEnoughBalance(); - - wbEth.approve(address(wbEth_Eth_Pool), _amount); - return wbEth_Eth_Pool.exchange(1, 0, _amount, _minOutputAmount); - } - function swapStEthToEth(uint256 _amount, uint256 _minOutputAmount) external returns (uint256) { if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); if (_amount > lido.balanceOf(address(this))) revert NotEnoughBalance(); @@ -427,19 +361,6 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab revert NotSupportedToken(); } - function verifyQueuedWithdrawal(address _user, IDelegationManager.Withdrawal calldata _queuedWithdrawal) public view returns (bytes32) { - require(_queuedWithdrawal.staker == _user && _queuedWithdrawal.withdrawer == address(this), "wrong depositor/withdrawer"); - for (uint256 i = 0; i < _queuedWithdrawal.strategies.length; i++) { - address token = address(_queuedWithdrawal.strategies[i].underlyingToken()); - require(tokenInfos[token].isWhitelisted && tokenInfos[token].strategy == _queuedWithdrawal.strategies[i], "NotWhitelisted"); - } - bytes32 withdrawalRoot = eigenLayerDelegationManager.calculateWithdrawalRoot(_queuedWithdrawal); - require(eigenLayerDelegationManager.pendingWithdrawals(withdrawalRoot), "WrongQ"); - require(!isRegisteredQueuedWithdrawals[withdrawalRoot], "Deposited"); - - return withdrawalRoot; - } - function isTokenWhitelisted(address _token) public view returns (bool) { return tokenInfos[_token].isWhitelisted; } @@ -479,10 +400,6 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab function getImplementation() external view returns (address) { return _getImplementation(); } - - function totalDeposited(address _token) public view returns (uint256) { - return tokenInfos[_token].totalDeposited; - } function isDepositCapReached(address _token, uint256 _amount) public view returns (bool) { uint256 amountOut = quoteByMarketValue(_token, _amount); @@ -490,51 +407,9 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab } /* INTERNAL FUNCTIONS */ - function _enqueueForWithdrawal(IStrategy[] memory _strategies, uint256[] memory _shares) internal returns (uint256) { - uint256 numStrategies = _strategies.length; - uint256 amount = 0; - for (uint256 i = 0; i < numStrategies; i++) { - IStrategy strategy = _strategies[i]; - uint256 share = _shares[i]; - address token = address(strategy.underlyingToken()); - uint256 dx = quoteStrategyShareForDeposit(token, strategy, share); - - // discount - dx = (10000 - tokenInfos[token].discountInBasisPoints) * dx / 10000; - - // Disable it because the deposit through EL queued withdrawal will be deprecated by EigenLayer anyway - // But, we need to still support '_enqueueForWithdrawal' as the backward compatibility for the already queued ones - // require(!isDepositCapReached(token, dx), "CAPPED"); - - amount += dx; - tokenInfos[token].strategyShare += uint128(share); - - _afterDeposit(token, amount); - - emit Liquified(msg.sender, dx, token, true); - } - return amount; - } - - function _completeWithdrawals(IDelegationManager.Withdrawal memory _queuedWithdrawal) internal { - bytes32 withdrawalRoot = eigenLayerDelegationManager.calculateWithdrawalRoot(_queuedWithdrawal); - if (!isRegisteredQueuedWithdrawals[withdrawalRoot]) revert NotRegistered(); - - uint256 numStrategies = _queuedWithdrawal.strategies.length; - for (uint256 i = 0; i < numStrategies; i++) { - address token = address(_queuedWithdrawal.strategies[i].underlyingToken()); - uint128 share = uint128(_queuedWithdrawal.shares[i]); - - if (tokenInfos[token].strategyShare < share) revert StrategyShareNotEnough(); - tokenInfos[token].strategyShare -= share; - } - - emit CompletedQueuedWithdrawal(withdrawalRoot); - } function _afterDeposit(address _token, uint256 _amount) internal { TokenInfo storage info = tokenInfos[_token]; - info.totalDeposited += uint96(_amount); } function _consumeRate(address _tokenIn, uint256 _tokenInAmount, uint256 _tokenOutAmount) internal { @@ -555,5 +430,7 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab if (!sent) revert EthTransferFailed(); } + receive() external payable {} + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} } diff --git a/test/Liquifier.t.sol b/test/Liquifier.t.sol index fe37c8c4..024f8099 100644 --- a/test/Liquifier.t.sol +++ b/test/Liquifier.t.sol @@ -186,169 +186,6 @@ contract LiquifierTest is TestSetup { vm.stopPrank(); } - function test_withdrawal_of_restaked_wBETH_succeeds() internal { - _setUp(MAINNET_FORK); - - vm.deal(alice, 100 ether); - vm.startPrank(alice); - wbEth.deposit{value: 20 ether}(address(0)); - wbEth.approve(address(eigenLayerStrategyManager), 20 ether); - - eigenLayerStrategyManager.depositIntoStrategy(wbEthStrategy, wbEthStrategy.underlyingToken(), 10 ether); - - IDelegationManager.Withdrawal memory queuedWithdrawal = _get_queued_withdrawal_of_restaked_LST_before_m2(wbEthStrategy); - - _complete_queued_withdrawal_V2(queuedWithdrawal, wbEthStrategy); - - uint256[] memory reqIds = liquifierInstance.stEthRequestWithdrawal(); - vm.stopPrank(); - - _finalizeLidoWithdrawals(reqIds); - } - - function test_erc20_queued_withdrawal_v2() public { - initializeRealisticFork(MAINNET_FORK); - setUpLiquifier(MAINNET_FORK); - - uint256 liquifierTVL = liquifierInstance.getTotalPooledEther(); - uint256 lpTvl = liquidityPoolInstance.getTotalPooledEther(); - - // While this unit test works, after EL m2 upgrade, - // this flow will be deprecated because setting 'wtihdrawer' != msg.sender won't be allowed within `queueWithdrawals` - address actor = address(liquifierInstance); - - vm.deal(actor, 100 ether); - vm.startPrank(actor); - stEth.submit{value: 1 ether}(address(0)); - - stEth.approve(address(eigenLayerStrategyManager), 1 ether); - eigenLayerStrategyManager.depositIntoStrategy(stEthStrategy, stEthStrategy.underlyingToken(), 1 ether); - - - uint256[] memory strategyIndexes = new uint256[](1); - IStrategy[] memory strategies = new IStrategy[](1); - uint256[] memory shares = new uint256[](1); - strategyIndexes[0] = 0; - strategies[0] = stEthStrategy; - shares[0] = stEthStrategy.shares(actor); - - // Queue withdrawal - IDelegationManager.QueuedWithdrawalParams[] memory queuedWithdrawalParams = new IDelegationManager.QueuedWithdrawalParams[](1); - queuedWithdrawalParams[0] = IDelegationManager.QueuedWithdrawalParams({ - strategies: strategies, - shares: shares, - withdrawer: actor - }); - - IDelegationManager.Withdrawal[] memory queuedWithdrawals = new IDelegationManager.Withdrawal[](1); - queuedWithdrawals[0] = IDelegationManager.Withdrawal({ - staker: actor, - delegatedTo: address(0), - withdrawer: actor, - nonce: uint96(eigenLayerDelegationManager.cumulativeWithdrawalsQueued(actor)), - startBlock: uint32(block.number), - strategies: strategies, - shares: shares - }); - - bytes32[] memory withdrawalRoots = eigenLayerDelegationManager.queueWithdrawals(queuedWithdrawalParams); - bytes32 withdrawalRoot = withdrawalRoots[0]; - - assertTrue(eigenLayerDelegationManager.pendingWithdrawals(withdrawalRoot)); - - liquifierInstance.depositWithQueuedWithdrawal(queuedWithdrawals[0], address(0)); - vm.stopPrank(); - - vm.roll(block.number + 7 days); - - IERC20[][] memory tokens = new IERC20[][](1); - tokens[0] = new IERC20[](1); - tokens[0][0] = stEthStrategy.underlyingToken(); - uint256[] memory middlewareTimesIndexes = new uint256[](1); - middlewareTimesIndexes[0] = 0; - bool[] memory receiveAsTokens = new bool[](1); - receiveAsTokens[0] = true; - - vm.startPrank(alice); - liquifierInstance.completeQueuedWithdrawals(queuedWithdrawals, tokens, middlewareTimesIndexes); - vm.stopPrank(); - } - - function _get_queued_withdrawal_of_restaked_LST_before_m2(IStrategy strategy) internal returns (IDelegationManager.Withdrawal memory) { - uint256[] memory strategyIndexes = new uint256[](1); - IStrategy[] memory strategies = new IStrategy[](1); - uint256[] memory shares = new uint256[](1); - strategyIndexes[0] = 0; - strategies[0] = strategy; - shares[0] = strategy.shares(alice); - - uint32 startBlock = uint32(block.number); - uint96 nonce = uint96(eigenLayerStrategyManager.numWithdrawalsQueued(alice)); - - // Step 1 - Queued withdrawal - bytes32 withdrawalRoot = eigenLayerStrategyManager.queueWithdrawal(strategyIndexes, strategies, shares, address(liquifierInstance), true); - assertEq(eigenLayerStrategyManager.withdrawalRootPending(withdrawalRoot), true); - vm.stopPrank(); - - _perform_eigenlayer_upgrade(); - - uint256 newNonce = eigenLayerDelegationManager.cumulativeWithdrawalsQueued(alice); - - vm.startPrank(alice); - - // Step 2 - Mint eETH - IDelegationManager.Withdrawal memory queuedWithdrawal = IDelegationManager.Withdrawal({ - staker: alice, - delegatedTo: address(0), - withdrawer: address(liquifierInstance), - nonce: newNonce, - startBlock: startBlock, - strategies: strategies, - shares: shares - }); - - // Before migration - vm.expectRevert("WrongQ"); - liquifierInstance.depositWithQueuedWithdrawal(queuedWithdrawal, address(0)); - - IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToMigrate = new IStrategyManager.DeprecatedStruct_QueuedWithdrawal[](1); - withdrawalsToMigrate[0] = IStrategyManager.DeprecatedStruct_QueuedWithdrawal({ - strategies: strategies, - shares: shares, - staker: alice, - withdrawerAndNonce: IStrategyManager.DeprecatedStruct_WithdrawerAndNonce({ - withdrawer: address(liquifierInstance), - nonce: nonce - }), - withdrawalStartBlock: startBlock, - delegatedAddress: address(0) - }); - assertEq(eigenLayerStrategyManager.calculateWithdrawalRoot(withdrawalsToMigrate[0]), withdrawalRoot); - - eigenLayerDelegationManager.migrateQueuedWithdrawals(withdrawalsToMigrate); - - IDelegationManager.Withdrawal memory migratedWithdrawal = IDelegationManager.Withdrawal({ - staker: withdrawalsToMigrate[0].staker, - delegatedTo: withdrawalsToMigrate[0].delegatedAddress, - withdrawer: withdrawalsToMigrate[0].withdrawerAndNonce.withdrawer, - nonce: withdrawalsToMigrate[0].withdrawerAndNonce.nonce, - startBlock: withdrawalsToMigrate[0].withdrawalStartBlock, - strategies: withdrawalsToMigrate[0].strategies, - shares: withdrawalsToMigrate[0].shares - }); - - bytes32 newWithdrawalRoot = eigenLayerDelegationManager.calculateWithdrawalRoot(migratedWithdrawal); - assertEq(eigenLayerDelegationManager.pendingWithdrawals(newWithdrawalRoot), true); - - liquifierInstance.depositWithQueuedWithdrawal(queuedWithdrawal, address(0)); - - // multipme mints using the same queued withdrawal fails - vm.expectRevert("Deposited"); - liquifierInstance.depositWithQueuedWithdrawal(queuedWithdrawal, address(0)); - - return queuedWithdrawal; - } - function _enable_deposit(address _strategy) internal { IEigenLayerStrategyTVLLimits strategyTVLLimits = IEigenLayerStrategyTVLLimits(_strategy); @@ -360,39 +197,6 @@ contract LiquifierTest is TestSetup { vm.stopPrank(); } - function _complete_queued_withdrawal(IStrategyManager.DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal, IStrategy strategy) internal { - vm.roll(block.number + 7 days); - - IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory queuedWithdrawals = new IStrategyManager.DeprecatedStruct_QueuedWithdrawal[](1); - queuedWithdrawals[0] = queuedWithdrawal; - IERC20[][] memory tokens = new IERC20[][](1); - tokens[0] = new IERC20[](1); - tokens[0][0] = strategy.underlyingToken(); - uint256[] memory middlewareTimesIndexes = new uint256[](1); - middlewareTimesIndexes[0] = 0; - // liquifierInstance.completeQueuedWithdrawals(queuedWithdrawals, tokens, middlewareTimesIndexes); - - vm.expectRevert(); - // liquifierInstance.completeQueuedWithdrawals(queuedWithdrawals, tokens, middlewareTimesIndexes); - } - - function _complete_queued_withdrawal_V2(IDelegationManager.Withdrawal memory queuedWithdrawal, IStrategy strategy) internal { - vm.roll(block.number + 7 days); - - IDelegationManager.Withdrawal[] memory queuedWithdrawals = new IDelegationManager.Withdrawal[](1); - queuedWithdrawals[0] = queuedWithdrawal; - IERC20[][] memory tokens = new IERC20[][](1); - tokens[0] = new IERC20[](1); - tokens[0][0] = strategy.underlyingToken(); - uint256[] memory middlewareTimesIndexes = new uint256[](1); - middlewareTimesIndexes[0] = 0; - liquifierInstance.completeQueuedWithdrawals(queuedWithdrawals, tokens, middlewareTimesIndexes); - - vm.expectRevert(); - liquifierInstance.completeQueuedWithdrawals(queuedWithdrawals, tokens, middlewareTimesIndexes); - } - - function test_pancacke_wbETH_swap() internal { initializeRealisticFork(MAINNET_FORK); setUpLiquifier(MAINNET_FORK); @@ -589,7 +393,6 @@ contract LiquifierTest is TestSetup { initializeRealisticFork(MAINNET_FORK); bool isTokenWhitelisted = liquifierInstance.isTokenWhitelisted(address(stEth)); - uint256 totalDeposited = liquifierInstance.totalDeposited(address(stEth)); uint256 getTotalPooledEther = liquifierInstance.getTotalPooledEther(address(stEth)); // Do the upgrade @@ -597,7 +400,6 @@ contract LiquifierTest is TestSetup { assertEq(liquifierInstance.isTokenWhitelisted(address(stEth)), isTokenWhitelisted); assertEq(liquifierInstance.isL2Eth(address(stEth)), false); - assertEq(liquifierInstance.totalDeposited(address(stEth)), totalDeposited); assertEq(liquifierInstance.getTotalPooledEther(address(stEth)), getTotalPooledEther); } From 69f76f6ea7f6c8c8982a1ae28c0e1c4347a3c435 Mon Sep 17 00:00:00 2001 From: jtfirek Date: Wed, 28 Aug 2024 14:03:47 -0500 Subject: [PATCH 2/3] adding back cb and wb logic --- src/Liquifier.sol | 46 ++++++++++++++++++++--------------- src/interfaces/ILiquifier.sol | 2 +- test/Liquifier.t.sol | 7 +++++- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/Liquifier.sol b/src/Liquifier.sol index 3a7f4c90..68e30795 100644 --- a/src/Liquifier.sol +++ b/src/Liquifier.sol @@ -256,10 +256,8 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab if (_isL2Eth) { if (_token == address(0) || _target != address(0)) revert(); dummies.push(IERC20(_token)); - } else { - // _target = EigenLayer's Strategy contract - if (_token != address(IStrategy(_target).underlyingToken())) revert NotSupportedToken(); - } + } + tokenInfos[_token] = TokenInfo(0, 0, IStrategy(_target), _isWhitelisted, _discountInBasisPoints, uint32(block.timestamp), 0, 0, 0, 0, _isL2Eth); } @@ -317,6 +315,22 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab if (currentBalance < _minOutputAmount + beforeBalance) revert WrongOutput(); } + function swapCbEthToEth(uint256 _amount, uint256 _minOutputAmount) external returns (uint256) { + if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + if (_amount > cbEth.balanceOf(address(this))) revert NotEnoughBalance(); + + cbEth.approve(address(cbEth_Eth_Pool), _amount); + return cbEth_Eth_Pool.exchange_underlying(1, 0, _amount, _minOutputAmount); + } + + function swapWbEthToEth(uint256 _amount, uint256 _minOutputAmount) external returns (uint256) { + if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + if (_amount > wbEth.balanceOf(address(this))) revert NotEnoughBalance(); + + wbEth.approve(address(wbEth_Eth_Pool), _amount); + return wbEth_Eth_Pool.exchange(1, 0, _amount, _minOutputAmount); + } + function swapStEthToEth(uint256 _amount, uint256 _minOutputAmount) external returns (uint256) { if (!roleRegistry.hasRole(LIQUIFIER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); if (_amount > lido.balanceOf(address(this))) revert NotEnoughBalance(); @@ -339,11 +353,6 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab revert NotSupportedToken(); } - function quoteStrategyShareForDeposit(address _token, IStrategy _strategy, uint256 _share) public view returns (uint256) { - uint256 tokenAmount = _strategy.sharesToUnderlyingView(_share); - return quoteByMarketValue(_token, tokenAmount); - } - function quoteByMarketValue(address _token, uint256 _amount) public view returns (uint256) { if (!isTokenWhitelisted(_token)) revert NotSupportedToken(); @@ -377,24 +386,21 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab total += liquidityPool.eETH().balanceOf(address(this)); } - /// deposited (restaked) ETH can have 3 states: - /// - restaked in EigenLayer & pending for withdrawals - /// - non-restaked & held by this contract - /// - non-restaked & not held by this contract & pending for withdrawals - function getTotalPooledEtherSplits(address _token) public view returns (uint256 restaked, uint256 holding, uint256 pendingForWithdrawals) { + /// deposited (restaked) ETH can have 2 states: + /// - held by this contract + /// - not held by this contract & pending for withdrawals + function getTotalPooledEtherSplits(address _token) public view returns (uint256 holding, uint256 pendingForWithdrawals) { TokenInfo memory info = tokenInfos[_token]; - if (!isTokenWhitelisted(_token)) return (0, 0, 0); + if (!isTokenWhitelisted(_token)) return (0, 0); - if (info.strategy != IStrategy(address(0))) { - restaked = quoteByFairValue(_token, info.strategy.sharesToUnderlyingView(info.strategyShare)); /// restaked & pending for withdrawals - } holding = quoteByFairValue(_token, IERC20(_token).balanceOf(address(this))); /// eth value for erc20 holdings pendingForWithdrawals = info.ethAmountPendingForWithdrawals; /// eth pending for withdrawals } function getTotalPooledEther(address _token) public view returns (uint256) { - (uint256 restaked, uint256 holding, uint256 pendingForWithdrawals) = getTotalPooledEtherSplits(_token); - return restaked + holding + pendingForWithdrawals; + (uint256 holding, uint256 pendingForWithdrawals) = getTotalPooledEtherSplits(_token); + + return holding + pendingForWithdrawals; } function getImplementation() external view returns (address) { diff --git a/src/interfaces/ILiquifier.sol b/src/interfaces/ILiquifier.sol index 49f41a38..66bc3a2c 100644 --- a/src/interfaces/ILiquifier.sol +++ b/src/interfaces/ILiquifier.sol @@ -106,4 +106,4 @@ interface ILiquifier { uint96 totalDeposited; bool isL2Eth; } -} \ No newline at end of file +} diff --git a/test/Liquifier.t.sol b/test/Liquifier.t.sol index 024f8099..2ca15da9 100644 --- a/test/Liquifier.t.sol +++ b/test/Liquifier.t.sol @@ -398,9 +398,14 @@ contract LiquifierTest is TestSetup { // Do the upgrade setUpLiquifier(MAINNET_FORK); + uint256 dustELWithdrawAmount = 0; + (uint128 strategyShare,,IStrategy strategy,,,,,,,,) = liquifierInstance.tokenInfos(address(stEth)); + dustELWithdrawAmount = liquifierInstance.quoteByFairValue(address(stEth), strategy.sharesToUnderlyingView(strategyShare)); + assertEq(liquifierInstance.isTokenWhitelisted(address(stEth)), isTokenWhitelisted); assertEq(liquifierInstance.isL2Eth(address(stEth)), false); - assertEq(liquifierInstance.getTotalPooledEther(address(stEth)), getTotalPooledEther); + // Accounting error resulted in `strategyShare` for stETH still having a value even though the withdrawal queue has been cleared + assertEq(liquifierInstance.getTotalPooledEther(address(stEth)), getTotalPooledEther - dustELWithdrawAmount); } function test_pauser() public { From 39680b4a8f68c3ccba11c66a2cb147d00a122972 Mon Sep 17 00:00:00 2001 From: jtfirek Date: Wed, 28 Aug 2024 14:31:18 -0500 Subject: [PATCH 3/3] comment fix --- src/Liquifier.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Liquifier.sol b/src/Liquifier.sol index 68e30795..8cfb87e1 100644 --- a/src/Liquifier.sol +++ b/src/Liquifier.sol @@ -386,7 +386,7 @@ contract Liquifier is Initializable, UUPSUpgradeable, OwnableUpgradeable, Pausab total += liquidityPool.eETH().balanceOf(address(this)); } - /// deposited (restaked) ETH can have 2 states: + /// deposited ETH can have 2 states: /// - held by this contract /// - not held by this contract & pending for withdrawals function getTotalPooledEtherSplits(address _token) public view returns (uint256 holding, uint256 pendingForWithdrawals) {