xanoutas/solidity-security-findings huggingface dataset:
warRoom - Incorrect Handling of Rewards During Asset Withdrawal from Destinations
Audit: 2023-06-tokemak-judging
Severity: HIGH

warRoom

high

# Incorrect Handling of Rewards During Asset Withdrawal from Destinations
## Summary
The `_withdraw` function contains an issue in the way rewards accrued during asset withdrawal from destination vaults are accounted for. Specifically, when the assets pulled are greater than the assets intended to be pulled (`info.totalAssetsPulled > info.totalAssetsToPull`), the code incorrectly calculates the `idleIncrease` by subtracting the intended assets from the pulled assets. This approach fails to account for the rewards earned during the process, potentially leading to inaccurate `idleIncrease` and potential financial losses.
## Vulnerability Detail
Within the _withdraw function, rewards are earned during the withdrawal process while burning the shares in destination vaults. This reward should be accounted in `totalIdle`
 
This is done correctly here
https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVault.sol#L485-L489

But when `info.totalAssetsPulled` exceeds `info.totalAssetsToPull`, the code calculates idleIncrease as follows:
https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVault.sol#L493-L494

However, this calculation does not consider the rewards that were received during the withdrawal([`see line 487`](https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVault.sol#L487)). As a result, the `idleIncrease` value does not accurately reflect the actual rewards earned.
## Impact
Current logic fails to account for the rewards earned during the withdrawal process, potentially leading to inaccurate `idleIncrease` and potential financial losses
## Code Snippet
https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVault.sol#L493-L494
## Tool used

Manual Review

## Recommendation
Following chnages should be implementaed
```solidity
    if (info.totalAssetsPulled > info.totalAssetsToPull) {
----        info.idleIncrease = info.totalAssetsPulled - info.totalAssetsToPull; 
+++        info.idleIncrease += info.totalAssetsPulled - info.totalAssetsToPull;
```



Duplicate of #5
ctf_sec - Malicious user can claim the reward for contract to make the user lose convex reward
Audit: 2023-06-tokemak-judging
Severity: HIGH

ctf_sec

high

# Malicious user can claim the reward for contract to make the user lose convex reward
## Summary

Malicious user can claim the reward for contract to make the user lose convex reward

## Vulnerability Detail
in ConvexRewardsAdapter the `_claimRewards` function works in the following way.
1. first the function records the balance of an account and stores it 
2. the function then calls the getReward function 
3. now the balance after is recorded.
4. finally the contract subracts the balanceAfter- balanceBefore

we can observe this in the logic
```solidity
        // get balances before
        for (uint256 i = 0; i < totalLength; ++i) {
            // Using the totalSupply check to represent stash tokens. They sometimes
            // stand in as the rewardToken but they don't have a "balanceOf()"
            if (IERC20(rewardTokens[i]).totalSupply() > 0) {
                balancesBefore[i] = IERC20(rewardTokens[i]).balanceOf(account);
            }
        }

        // claim rewards
        bool result = rewardPool.getReward(account, true);
        if (!result) {
            revert RewardAdapter.ClaimRewardsFailed();
        }

        // get balances after and calculate amounts claimed
        for (uint256 i = 0; i < totalLength; ++i) {
            uint256 balance = 0;
            // Same check for "stash tokens"
            if (IERC20(rewardTokens[i]).totalSupply() > 0) {
                balance = IERC20(rewardTokens[i]).balanceOf(account);
            }

            amountsClaimed[i] = balance - balancesBefore[i];

            if (sendTo != address(this) && amountsClaimed[i] > 0) {
                IERC20(rewardTokens[i]).safeTransfer(sendTo, amountsClaimed[i]);
            }
        }

        RewardAdapter.emitRewardsClaimed(rewardTokens, amountsClaimed);

        return (amountsClaimed, rewardTokens);
    }
```

This is a big problem because the function `getReward`, is public and callable by anyone. A malicious user can take adavantage of this in the following way.
1. malicious user sees `_claimReward` function in the mempool
2. malicious user frontruns this tx with `getReward` to clailm the reward for the contract in this [line of code](https://etherscan.io/address/0xCF50b810E57Ac33B91dCF525C6ddd9881B139332#code#L968)

```solidity
    function getReward(address _account, bool _claimExtras, bool _stake) public updateReward(_account){
```

4. rewards are sent to the contract
5. contract then now finally executes `_claimReward` but it is too late
6. in the `_claimReward` balance before is stored includes the reward claimed by other user
7. `_getReward` is now called but no rewards are present, so none are sent
8. balanceAfter is now recorded and because there was no change the contract will state that there are 0 rewards for the user.
9. users rewards are now stuck in the contract

similar past finding:

https://github.com/sherlock-audit/2023-03-notional-judging/issues/168

## Impact
A users funds are lost and because the cost
ctf_sec - Mav oracle LP price can be manipulated
Audit: 2023-06-tokemak-judging
Severity: HIGH

ctf_sec

high

# Mav oracle LP price can be manipulated
## Summary
Mav oracle can be manipulated
## Vulnerability Detail
The mav oracle can easily be manipulated because of its use of spot price

```solidity
// Get reserves in boosted position.
(uint256 reserveTokenA, uint256 reserveTokenB) = boostedPosition.getReserves();
```
The above call can easily be manipulated by an attacker. A malicious user can get a flash loan and make a swap to artificially inflate the `reserveTokenA` and the `reserveTokenB` amounts.

These amounts are later used in the function to determine the value of the maverick LP token. but because the values of of `reserveTokenA` and `reserveTokenB` can be easily manipulated, the price of the maverick LP token will be over valued.
```solidity
 uint256 priceInEthTokenA = rootPriceOracle.getPriceInEth(address(pool.tokenA()));
        uint256 priceInEthTokenB = rootPriceOracle.getPriceInEth(address(pool.tokenB()));

        // Calculate total value of each token in boosted position.
        uint256 totalBoostedPositionValueTokenA = reserveTokenA * priceInEthTokenA;
        uint256 totalBoostedPositionValueTokenB = reserveTokenB * priceInEthTokenB;
```
## Impact
Attacker can inflate the price of the maverick LP token to drain funds from the protocol.
## Code Snippet
https://github.com/Tokemak/v2-core-audit-2023-07-14/blob/62445b8ee3365611534c96aef189642b721693bf/src/oracles/providers/MavEthOracle.sol#L44-L78
## Tool used

Manual Review

## Recommendation
do not use spot price to determine value

Duplicate of #635
warRoom - User can not deposit ETH in LMPVault via router without being at loss
Audit: 2023-06-tokemak-judging
Severity: HIGH

warRoom

high

# User can not deposit ETH in LMPVault via router without being at loss
## Summary
`deposit()` function of the router collects ETH and WETH both while issuing the share for only one of them. If the user transfers eth and has also given the WETH approval for the deposit, the router will take both ETH and WETH, but it will only issue share for only one of them and in case user has only provided the ETH, the transaction will always revert because it will try to capture wETH as well which user has not approved of. Resulting in the user can only supply WETH to participate. 

## Vulnerability Detail

[`deposit()`](https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVaultRouterBase.sol#L44) function of the `LMPVaultRouterBase.sol` tries to collect WETH and ETH from the user, with [`_processEthIn()`](https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVaultRouterBase.sol#L51) Ether is deposited and then with [`pullToken()`](https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVaultRouterBase.sol#L54) WETH is deposited. 

[`LMPVaultRouterBase.deposit#L44`](https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVaultRouterBase.sol#L44)

```solidity
    function deposit(
        ILMPVault vault,
        address to,
        uint256 amount,
        uint256 minSharesOut
    ) public payable virtual override returns (uint256 sharesOut) {
        // handle possible eth
        _processEthIn(vault);

        IERC20 vaultAsset = IERC20(vault.asset());
        pullToken(vaultAsset, amount, address(this));
        return _deposit(vault, to, amount, minSharesOut);
    }


```
[`LMPVaultRouterBase._processEthIn#L111-L122`](https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVaultRouterBase.sol#L111-L122)
```solidity
    function _processEthIn(ILMPVault vault) internal {
        // if any eth sent, wrap it first
        if (msg.value > 0) {
            // if asset is not weth, revert
            if (address(vault.asset()) != address(weth9)) {
                revert InvalidAsset();
            }

            // wrap eth
            weth9.deposit{ value: msg.value }();
        }
    }

```
[`PeripheryPayments.pullTokens#L54-L56`](https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/utils/PeripheryPayments.sol#L54-L56)

```solidity
    function pullToken(IERC20 token, uint256 amount, address recipient) public payable {
        token.safeTransferFrom(msg.sender, recipient, amount);
```


Due to this User can not deposit ETH in LMPVault via router without being at a loss, Consider the following scenario : 

1. Case 1: Alice Deposits only ETH
- In the first case, let's consider Alice who wants to deposit only ETH into the LMPVault. The process involves converting her ETH to WETH using the _processEthIn
TangYuanShen - DestinationVault rewards can always be stolen by anyone
Audit: 2023-06-tokemak-judging
Severity: HIGH

TangYuanShen

high

# DestinationVault rewards can always be stolen by anyone
## Summary

The LMPVault receives base assets rewards from the DestinationVault's MainRewarder, which increase the value of an LMPVault share. These rewards can be stolen each time by anyone using flash-staking.

## Vulnerability Detail

The LMPVault uses the base assets in the vault to obtain shares in a DestinationVault. Each DestinationVault also has a MainRewarder, which emits rewards in the vaults base assets.

These rewards are claimed in `LMPVault:_updateDebtReporting` and added to the `idleIncrease` value on lines 771-776:
```solidity
uint256 beforeBaseAsset = _baseAsset.balanceOf(address(this));
IMainRewarder(destVault.rewarder()).getReward(address(this), false);
uint256 claimedRewardValue = _baseAsset.balanceOf(address(this)) - beforeBaseAsset;
idleIncrease += claimedRewardValue;
```

The `idleIncrease` value is directly used in `LMPVault:totalAssets`, which again is used in `_convertToAssets` and `_convertToShares`:
```solidity
function totalAssets() public view override returns (uint256) {
    return totalIdle + totalDebt;
}
function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {
    uint256 supply = totalSupply();
    shares = (assets == 0 || supply == 0) ? assets : assets.mulDiv(supply, totalAssets(), rounding);
}
function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) {
    uint256 supply = totalSupply();
    assets = (supply == 0) ? shares : shares.mulDiv(totalAssets(), supply, rounding);
}
```

So this means that the increase of `idleIncrease` with the rewards will also increase the share rate and so the rewards are distributed among the stakers/holders of LMPVault shares.

The function that claims the rewards is callable from `LMPVault:updateDebtReporting`, which is permissionless and so it can be called by anyone at any time:
```solidity
function updateDebtReporting(address[] calldata _destinations) external nonReentrant trackNavOps {
    _updateDebtReporting(_destinations);
}
```

Because of this, the timing and state of the LMPVault could be controlled by an attacker, before calling the reward claiming function. Because the LMPVault does not have withdrawal queue or unstake delay, it becomes possible to do this using flash-loan staking.

The first LMPVault will use a base asset of WETH, which can be easily flash-loaned from various places.

In a single transaction, the attacker could:
- Flash-loan 100m+ amount of WETH.
- Deposit the WETH into the LMPVault and obtain >99% of all shares. 
- (The max per wallet restriction can be easily bypassed by using multiple contracts).
- Call `updateDebtReporting`, which will claim the rewards and increase the share rate.
- Withdraw all of the shares, which will return the 100m+ WETH + >99% of the DestinationVault's claimed rewards.

The above scenario can also be repeated each time.

## Impact

By
TangYuanShen - LMPVaultRouter will charge a user twice and the extra funds can be stolen
Audit: 2023-06-tokemak-judging
Severity: HIGH

TangYuanShen

high

# LMPVaultRouter will charge a user twice and the extra funds can be stolen
## Summary

The LMPVaultRouter will charge a user twice for a deposit or mint to an LMPVault, if the user wants to use ETH. This extra ETH can then be stolen by an attacker.

## Vulnerability Detail

The LMPVaultRouter inherits from LMPVaultRouterBase, which exposes the functions `deposit` and `mint` where a user can deposit assets to an LMPVault.

Both functions use `_processEthIn` which will wrap `msg.value` to WETH, in case the user wants to use native ETH:
```solidity
function _processEthIn(ILMPVault vault) internal {
    // if any eth sent, wrap it first
    if (msg.value > 0) {
        // if asset is not weth, revert
        if (address(vault.asset()) != address(weth9)) {
            revert InvalidAsset();
        }

        // wrap eth
        weth9.deposit{ value: msg.value }();
    }
}
```
But after this call, the functions will still try to pull the tokens from the user (which would be WETH):
```solidity
IERC20 vaultAsset = IERC20(vault.asset());
pullToken(vaultAsset, amount, address(this));
```
As a result, if the user sends ETH, they will be charged twice, once in ETH, once in WETH. Only the specific amount will be used and so extra WETH will stay in the contract.

The contract also inherits from PaymentPeriphery, which would allow an attacker to immediately pull the extra WETH from the contract after such as a mistake, using `sweepToken`:

```solidity
function sweepToken(IERC20 token, uint256 amountMinimum, address recipient) public payable {
    uint256 balanceToken = token.balanceOf(address(this));
    if (balanceToken < amountMinimum) revert InsufficientToken();

    if (balanceToken > 0) {
        token.safeTransfer(recipient, balanceToken);
    }
}
```

## Impact

The LMPVaultRouter will charge a user twice if ETH is used and the extra amount of WETH can be immediately stolen by an attacker, resulting in a loss of user funds.

## Code Snippet

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVaultRouterBase.sol#L23-L57

## Tool used

Manual Review, VSCode

## Recommendation

The case of sending ETH should be correctly handled in `deposit` and `mint` by checking the balance of WETH or using some return value of `_processEthIn`.

Duplicate of #1
carrotsmuggler - Users can steal reward tokens due to bad update of rewardDebt in `LMPVault.sol`.
Audit: 2023-06-tokemak-judging
Severity: HIGH

carrotsmuggler

high

# Users can steal reward tokens due to bad update of rewardDebt in `LMPVault.sol`.
## Summary

Users can steal reward tokens due to bad update of rewardDebt in `LMPVault.sol`.

## Vulnerability Detail

The contract `LMPVault.sol` is an erc20 contract and the tokens are automatically staked in the rewarder contract to earn continuous rewards. The staking mechanism is carried out in the `_beforeTokenTransfer` and `_afterTokenTransfer` functions.

```solidity
function _beforeTokenTransfer(address from, address to, uint256 amount){
    rewarder.withdraw(from, amount, true);
}

function _afterTokenTransfer(address from, address to, uint256 amount){
    rewarder.stake(to, amount);
}
```

The `stake` and `withdraw` functions update the user's rewardDebt in the `Mainrewarder.sol` contract.

```solidity
function stake(address account, uint256 amount) public onlyStakeTracker {
    _updateReward(account); // <------------ Updates rewardDebt
    _stake(account, amount);
```

The issue is that the reward debt of the `to` user should also be updated in the `_beforeTokenTransfer` function, and not after the token transfer. This is because after the token transfer, the `to` user already has a non-zero balance, and their rewardDebt is calculated on a non-zero balance for the first time.

Assume Alice is sending 100 LMPVault tokens to Bob, and Bob has currently a balance of 0.

1. Bob gets transferred the 100 tokens. Now, balanceOf[Bob] = 100.
2. The `_afterTokenTransfer` function is called after the token transfer happens. The `rewarder.stake(Bob,100)` is called.
3. Inside the `stake` function, `_updateReward(Bob)` is called.
4. The `earnedRewards` value is calculated. This is done in the `earned(Bob)` function in `AbstractRewarder.sol` contract, as shown below.
5. As shown below, since Bob already has the tokens transferred, and has `balanceOf(Bob)` as non-zero.
6. Bob's `userRewardPerTokenPaid` is still 0, since this is their fisrt time calculating rewards. Thus Bob earns a large amount of rewards immediately after receiving the tokens.

```solidity
function earned(address account) public view returns (uint256) {
    return ((balanceOf(account) * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18) + rewards[account];
}
```

So Bob is able to get a bunch of rewards in the same block as their first transaction. This is because by the time `_updateReward` is called, Bob has already received the tokens and has a non-zero balance.

## Impact

Bad token accounting, allowing Bob to steal reward tokens.

## Code Snippet

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVault.sol#L854-L865

## Tool used

Manual Review

## Recommendation

Trigger `_updateReward` for Bob before transferring in the tokens. In the `_beforeTokenTransfer` function, add an update statement.

```solidity
function _beforeTokenTransfer(address from, address to, uint256 amount){
    rewarder.withdraw(from, amount, tru
carrotsmuggler - Incorrect idle value accounting in `LMPVault.sol` leads to lost tokens.
Audit: 2023-06-tokemak-judging
Severity: HIGH

carrotsmuggler

high

# Incorrect idle value accounting in `LMPVault.sol` leads to lost tokens.
## Summary

Incorrect idle value accounting in `LMPVault.sol` leads to lost tokens.

## Vulnerability Detail

The function `_withdraw` in `LMPVault.sol` is used to withdraw tokens from the vault. The function also calculates the change in the `idle` amount in the vault. In this context, `idle` refers to the assets sitting in the LMPVault, not assigned to any destination yet. The LMP vault pulls tokens from the destination vault, and then increases the idle amount to track these tokens now in the LMP vault.

```solidity
uint256 assetPreBal = _baseAsset.balanceOf(address(this));
uint256 assetPulled = destVault.withdrawBaseAsset(sharesToBurn, address(this));

// Destination Vault rewards will be transferred to us as part of burning out shares
// Back into what that amount is and make sure it gets into idle
info.idleIncrease += _baseAsset.balanceOf(address(this)) - assetPreBal - assetPulled;
```

More rewards can be received by the LMP vault than returned by the destination vault as part of burning shares. For this reason, these extra tokens are stored in the `info.idleIncrease` variable to keep track of them. The `extra` amount is calculated as the difference between the balance change, and the expected amount of tokens returned by the destination vault, `assetPulled`.

The issue is that this variable gets overwritten in the next if statement.

```solidity
if (info.totalAssetsPulled > info.totalAssetsToPull) {
    // @audit rewards overwritten!
    info.idleIncrease = info.totalAssetsPulled - info.totalAssetsToPull;
    info.totalAssetsPulled = info.totalAssetsToPull;
    break;
}
```

Now, the extra rewards stored in idleIncrease are overwritten by the difference between totalAssets pulled and assets expected to be pulled. Thus all the reward tokens which the contract was tracking up to now are overwritten and lost.

## Impact

The contract loses track of extra reward tokens from destination vaults.

## Code Snippet

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVault.sol#L482-L497

## Tool used

Manual Review

## Recommendation

Replace the `=` with `+=` in the statement to keep track of the extra reward tokens.

```solidity
info.idleIncrease += info.totalAssetsPulled - info.totalAssetsToPull;
```

Duplicate of #5