hats-finance / ether-fi-0x36c3b77853dec9c4a237a692623293223d4b9bc4

Smart Contracts for Ether Fi dapp
1 stars 1 forks source link

Invalid check in `LiquidityPool.withdraw` function prevents users from withdrawing their deposits #44

Open hats-bug-reporter[bot] opened 11 months ago

hats-bug-reporter[bot] commented 11 months ago

Github username: -- Twitter username: -- Submission hash (on-chain): 0x23b9bcafba7e25f79d43c2ff27224438035b75268999ab03888b375b5589354f Severity: medium

Description: Description\ Describe the context and the effect of the vulnerability.

Attack Scenario

LiquidityPool._deposit function

  ```solidity
  function _deposit() internal returns (uint256) {
        totalValueInLp += uint128(msg.value);
        uint256 share = _sharesForDepositAmount(msg.value);
        if (msg.value > type(uint128).max || msg.value == 0 || share == 0) revert InvalidAmount();

        eETH.mintShares(msg.sender, share);

        return share;
  }
  ```

LiquidityPool._sharesForDepositAmount function

  ```solidity
  function _sharesForDepositAmount(uint256 _depositAmount) internal view returns (uint256) {
        uint256 totalPooledEther = getTotalPooledEther() - _depositAmount;
        if (totalPooledEther == 0) {
              return _depositAmount;
        }
        return (_depositAmount * eETH.totalShares()) / totalPooledEther;
  }
  ```

Mitigation

LiquidityPool.withdraw function

    function withdraw(address _recipient, uint256 _amount) external whenNotPaused returns (uint256) {
        uint256 share = sharesForWithdrawalAmount(_amount);
        require(msg.sender == address(withdrawRequestNFT) || msg.sender == address(membershipManager), "Incorrect Caller");
-        if (totalValueInLp < _amount || (msg.sender == address(withdrawRequestNFT) && -ethAmountLockedForWithdrawal < _amount) || eETH.balanceOf(msg.sender) < _amount) revert InsufficientLiquidity();

+        if (totalValueInLp < _amount || (msg.sender == address(withdrawRequestNFT) && +ethAmountLockedForWithdrawal < _amount) || eETH.balanceOf(msg.sender) < share) revert InsufficientLiquidity();
        if (_amount > type(uint128).max || _amount == 0 || share == 0) revert InvalidAmount();

        totalValueInLp -= uint128(_amount);
        if (msg.sender == address(withdrawRequestNFT)) {
            ethAmountLockedForWithdrawal -= uint128(_amount);
        }

        eETH.burnShares(msg.sender, share);

        (bool sent, ) = _recipient.call{value: _amount}("");
        if (!sent) revert SendFail();

        return share;
    }
seongyun-ko commented 11 months ago

This statement is false:

"And since the pool can receive ethers from any one directly (by direct transfer of ethers without going through the deposit process); so the eETH.totalShares() and the totalPooledEther are not necessarily 1:1."

The totalPooledEther does not change by any direct ETH transfer to the LiquidityPool contract.

seongyun-ko commented 11 months ago

close