xanoutas/solidity-security-findings huggingface dataset:
The way you update the price of a token is wrong, because you don't use the price directly from an Oracle.
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L142-L174


# Vulnerability details

## Impact
Token prices will not be updated correctly.

## Proof of Concept
https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L142-L174

## Recommended Mitigation Steps
You should use an Oracle like Chainlink(https://docs.chain.link/data-feeds/using-data-feeds) to get the price of the tokens.


## Assessed type

Oracle
Inconsistent Token Value Handling in `_lock` Function 
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L311


# Vulnerability details

## Impact
The `_lock` function does not account for the different values of tokens (USDB, ETH, WETH) and treats the `_quantity` the same regardless of the token type. This can lead to incorrect calculations and potential vulnerabilities, as different tokens have different decimal places and values.

## Proof of Concept
In the `_lock` function, the quantity of tokens is treated uniformly without considering the specific properties of each token:
```solidity
function _lock(
    address _tokenContract,
    uint256 _quantity,
    address _tokenOwner,
    address _lockRecipient
) private {
    // No differentiation between USDB, ETH, WETH
    // Existing code...
}
```
## Tools Used
Manual Review
## Recommended Mitigation Steps
Convert the token quantity to its USD equivalent using the token's USD price before performing any calculations. This ensures that the quantity is treated correctly regardless of the token type.


## Assessed type

ERC20
When _tokenContract is not zero address, there is no check if msg.value matches quantity, msg.value is 0, or allowance is less than quantity
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L324-L331


# Vulnerability details

## Impact
msg.value can be less than quantity, or be 0, or allowance less than quantity

## Proof of Concept
The _lock function checks the msg.value only when _tokenContract is the zero address. However, it does not check if the msg.value is less than quantity or zero when _tokenContract is a valid token contract addres.

`if (_tokenContract == address(0)) {
    if (msg.value != _quantity) revert ETHValueIncorrectError();
} else {
    if (msg.value != 0) revert InvalidMessageValueError();
    // ...
}`

## Tools Used
Manual review

## Recommended Mitigation Steps
Also check mdh.value against quantity and zero, includig allowance not less than quantity when _tokenCotract is not address(0).


## Assessed type

Context
The less decimals a token has the more reward a user gets
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L461-L487


# Vulnerability details

## Impact

In getLockedWeightedValue() we are calculating the USD value of all tokens locked by a player, weighted by their yield. if we have locked a token with 6 decimals we will get more locked weighted value because we will have much more delta decimals than a token with 18 decimals. The more locked weighted we have value the more daily schnibbles we get 

## Proof of Concept

Example with 6-Decimal Token (USDC)
Decimals: 6
Price: $1
Quantity: 10,000

Example with 18-Decimal Token (ETH)
Decimals: 18
Price: $2,000
Quantity: 5

Let's calculate the lockedWeighted value for both scenarios.

6-Decimal Token (USDC)
Calculate deltaDecimal:

uint256 deltaDecimal = 10 ** (18 - 6); // 10^12 = 1,000,000,000,000

Values:
deltaDecimal: 1,000,000,000,000
quantity: 10,000
usdPrice: 1

Calculating lockedWeighted:
lockedWeighted += (1,000,000,000,000 * 10,000 * 1) / 1e18;

Performing the Calculation:

Multiplication:
1𝑒12×1𝑒4×1=1𝑒16

Division by 1e18:
1𝑒16/1𝑒18=0.01

So, the lockedWeighted value for the 6-decimal token (USDC) is 0.01.

18-Decimal Token (ETH)
Calculate `deltaDecimal:

uint256 deltaDecimal = 10 ** (18 - 18); // 10^0 = 1

Values:
deltaDecimal: 1
quantity: 5
usdPrice: 2,000

Calculating `lockedWeighted:
lockedWeighted += (1 * 5 * 2,000) / 1e18;
Performing the Calculation:

Multiplication:
1×5×2,000=10,000

Division by 1e18:
10,000/1e18=0.00000000001

So, the lockedWeighted value for the 18-decimal token (ETH) is 0.00000000001.

Summary
6-decimal token (USDC): lockedWeighted value is 0.01.
18-decimal token (ETH): lockedWeighted value is 0.00000000001.

In this scenario, even though both tokens have the same total value (price * quantity = $10,000), the lockedWeighted value is significantly higher for the 6-decimal token due to the impact of deltaDecimal. This example highlights how the decimal scaling affects the lockedWeighted calculation, making tokens with fewer decimals contribute more significantly.

## Tools Used

Manual review

## Recommended Mitigation Steps

Find a way to prevent this behaivour


## Assessed type

Error
The yield of `USDB` and `WETH` will be locked in the `LockManager`
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L311-L398
https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L401-L427


# Vulnerability details

## Impact
In Blast, `WETH` and `USDB` accounts have Automatic yield by default for both EOAs and smart contracts. The `LockManager` will use `WETH` and `USDB` as locked token. When users lock their tokens in `LockManager` contract, the `LockManager` contract will automatically receive `WETH` and `USDB` yields. When users unlock their tokens, these yields will not be sent to users, they will be locked in the `LockManager` contract forever.

## Proof of Concept
As per the doc of blast(https://docs.blast.io/building/guides/eth-yield), Smart contract accounts have three Yield Modes for their rebasing mode:
- Void (DEFAULT): ETH balance never changes; no yield is earned
- Automatic: native ETH balance rebases (increasing only)
- Claimable: ETH balance never changes; yield accumulates separately

For `WETH` and `USDB`, the Automatic yield is the default mode.(https://docs.blast.io/building/guides/weth-yield)

`Munchables` says that it will use `WETH` and `USDB` as locked token.

Users can call `lock` function to lock `USDB` and `WETH`. It will transfer tokens to the current contract and record the quantity:
```solidity
       // Transfer erc tokens
        if (_tokenContract != address(0)) {
            IERC20 token = IERC20(_tokenContract);
            token.transferFrom(_tokenOwner, address(this), _quantity);
        }

        lockedToken.remainder = remainder;
        lockedToken.quantity += _quantity;
        lockedToken.lastLockTime = uint32(block.timestamp);
        lockedToken.unlockTime =
            uint32(block.timestamp) +
            uint32(_lockDuration);
```
This `quantity` will not be changed when the `LockManager` receives yields from Blast.

When users call `unlock` function, it will only send `quantity` tokens to the users and ignore the yields:
```solidity
function unlock(
        address _tokenContract,
        uint256 _quantity
    ) external notPaused nonReentrant {
        LockedToken storage lockedToken = lockedTokens[msg.sender][
            _tokenContract
        ];
        if (lockedToken.quantity < _quantity)
            revert InsufficientLockAmountError();
        if (lockedToken.unlockTime > uint32(block.timestamp))
            revert TokenStillLockedError();

        // force harvest to make sure that they get the schnibbles that they are entitled to
        accountManager.forceHarvest(msg.sender);

        lockedToken.quantity -= _quantity;

        // send token
        if (_tokenContract == address(0)) {
            payable(msg.sender).transfer(_quantity);
        } else {
            IERC20 token = IERC20(_tokenContract);
            token.transfer(msg.sender, _quantity);
        }

        emit Unlocked(msg.sender, _tokenContract, _quantity);
    }
```
As a result, all yields wil
Wrong calculation of quantity and remainder leads to loss of fund
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

 https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L311


# Vulnerability details

## Impact
Due to wrong calculation of quantity and remainder, the user can lose their fund.

## Proof of Concept
Suppose the `nftCost` of a token is 100e18. An user locks this token by calling the function `lock` with the following parameters:
 - `_tokenContract` = token contract address
 - `_quantity` = 120e18

 In the function `_lock`, the variable `quantity` is set to 120e18, because `uint256 quantity = _quantity + lockedToken.remainder = 120e18 + 0 = 120e18`, assuming `remainder` is zero.
 ```solidity
    function _lock(
        address _tokenContract,
        uint256 _quantity,
        address _tokenOwner,
        address _lockRecipient
    ) private {

        //.....

        uint256 quantity = _quantity + lockedToken.remainder;

        //.....

        remainder = quantity % configuredToken.nftCost;
        numberNFTs = (quantity - remainder) / configuredToken.nftCost;

        //.....

        lockedToken.remainder = remainder;
        lockedToken.quantity += _quantity;

        //....
    }
 ```
 https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L311

 Since the `nftCost` is 100e18, the `remainder` will be 20e18, and `numberNFTs` will be one. At the end, we will have:
  - `lockedToken.remainder` = 20e18
  - `lockedToken.quantity` = 100e18

Later the user locks another **80e18** token to use sum of this amount and the remainder to reveal another NFT. This time, the variable `quantity` is set to 100e18, because `uint256 quantity = _quantity + lockedToken.remainder = 80e18 + 20e18 = 100e18`, and the `remainder` will be zero, and one more NFT is revealed for the user. At the end we will have:
  - `lockedToken.remainder` = 0
  - `lockedToken.quantity` = 100e18 + 80e18 = 180e18

**The issue is that the `lockedToken.quantity` and `lockedToken.remainder` are set to 180e18 and 0, respectively, while the user so far locked 200e18.**

Because of this issue, when the user would like to unlock, he is only able to unlock and withdraw 180e18 token, and he loses 20e18.
```solidity
    function unlock(
        address _tokenContract,
        uint256 _quantity
    ) external notPaused nonReentrant {
        //....

        lockedToken.quantity -= _quantity;

        //....
    }
```
https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L416

**The root cause of this issue is that `lockedToken.quantity` is updated based on `_quantity` which is the function parameter, while the contribution of `lockedToken.remainder` to the quantity is missed. In other words, the quantity set for the user misses the remainder part.**


## Tools Used

## Recommended Mitigation Steps
Modify the code as follows:
 ```solidity
    function _lock(
        address _tokenContract,
        uint256 _quantity,
        address _tokenOwner,
        address _lockRecipient
    ) p
Rebasing Tokens USDB and WETH aren't handled correctly by lockManager
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L275-L398


# Vulnerability details

## Summary

On the Blast chain, USDB and WETH are implemented as rebasing tokens that adjust holders' balances automatically to reflect yield changes. When these tokens are locked in the protocol, users miss out on potential yields due to the tokens' rebasing nature. More critically, during a negative rebase, the protocol may not possess sufficient funds to fulfill all unlock requests, potentially leading to transaction reverts and locked funds.

## Vulnerability Detail

USDB and WETH adjust user balances directly to account for accrued yield or to reflect negative rebasing adjustments. This automatic balance adjustment poses significant risks when these tokens are locked in the lockManager.

The locking mechanism involves transferring tokens from the user to the contract without accounting for future rebasing events:

- _lock
```solidity=347
if (_tokenContract != address(0)) {
    IERC20 token = IERC20(_tokenContract);
    token.transferFrom(_tokenOwner, address(this), _quantity);
}
```

When users request to unlock their tokens, the amount originally locked is returned without any adjustments for accrued yield or loss due to rebasing:

- unlock
```solidity=422
IERC20 token = IERC20(_tokenContract);
token.transfer(msg.sender, _quantity);
```

This process does not compensate for any increase in token balance due to positive rebasing, effectively causing users to lose out on yield. Moreover, in the event of a negative rebase, the lockManager may find itself with fewer tokens than required to satisfy all unlock requests, leading to failed transactions.

## Impact

This vulnerability can result in two significant issues for users:

1. Loss of Yield: Users do not benefit from positive rebasing effects during the lock period, missing out on potential gains.
2. Inability to Unlock Funds: During a negative rebase, the total token supply in the lockManager may decrease below the amount needed to honor all unlock requests, causing user transactions to revert.

## Tool used

Manual Review

## Recommendation

account for users their yield.


## Assessed type

ERC20
User will lose NFTs due to auto rebasing of USDB/WETH.
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L311


# Vulnerability details

## Impact
USDB/WETH are the native tokens of the blast chain that are being used in munchables.
Both USDB and WETH yield modes are AUTOMATIC by default, due to which the user's locked balance will keep increasing bit by bit due to this yield.
The problem lies in the [#L330](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L330C29-L330C38)due to this auto accruing yield not configured properly user potentially won't be able to lock funds.
Even if somehow the user is able to lock funds(by locking them prior to the auto-rebasing) he might lose on [number of NFTs](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L364)as the `quantity` data will be stale.
## Proof of Concept
**Scenario-1: User not able to lock funds**
The user tries to lock 100 WETH at 00:00 AM.
- Txn gets reverted due to an issue lying in [#L330](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L330C29-L330C38)
- Issue arises due to change in user's balance:
- After auto-rebasing **User's Balance would be:100.010958904**
>*The above calculation comes from [here](https://docs.blast.io/building/guides/eth-yield)*

**Scenario-2: User getting less number of NFTs**
> According to the docs of [blast eth-yieldhttps://docs.blast.io/building/guides/eth-yield)
a contract gets a 4% yield on the contract's balance if the duration is one year.
> *Due to [quantity](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L344) taking only the sending amount in the accounting.
The number of NFTs that the user will be getting will be low even if the user locks his tokens for a year. It will be according to the old locked balance that he locked one year ago, even though the amount of tokens will be 4% more than the original.*

- User locked 100 weth on 1 Jan 2024 for 365 days.
- The number of Nfts that the user will get during the lockdrop will be according to the 100 WETH that is being used in the accounting process instead of using the user's locked balance.


## Tools Used
Manual review
## Recommended Mitigation Steps
Configure the auto-rebasing of USDB/WETH and decide, do you want it or not.
If yes, change the accounting calculation and make them intake user's locked balance for NFTs distribution.
If no, configure it.





## Assessed type

Context
unlock with partial quantity can cause user loss of rewards.
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L414


# Vulnerability details

## Impact
unlock with partial quantity will reset the last harvest time in account manager.
So, the user will lose rewards ...etc.

## Proof of Concept
forceHarvest at [accountManager.forceHarvest(msg.sender)](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L414) can be an issue since it updates [lastHarvestDate](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/AccountManager.sol#L382) to the current block.timestamp.

A user called [unlock](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L401) with partial `_quantity`, a `forceHarvest` is done at:
```
accountManager.forceHarvest(msg.sender);
```
and now all the rewards harvested according to [secondsToClaim](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/AccountManager.sol#L376), which is:
```
block.timestamp - players[_caller].lastHarvestDate
```
The issue here when a user try to claim the remaining of lockedToken.quantity, there will be no harvest rewards for it, and users now need to wait for a period of time so they can harvest rewards for the remining quantity.

Assuming Bob has lockedToken.quantity = 100e18.
- Bob calls [unlock](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L401) for _quantity as 10e18.
- A forceHarvest is done for 10e18 according to [secondsToClaim](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/AccountManager.sol#L376)
- [lastHarvestDate](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/AccountManager.sol#L382) now is updated to the current block.timestamp.
- Bob calls `unlock` again for the remaining _quantity as 90e18, but doesn't harvest any rewards, since forceHarvest relys on `lastHarvestDate`.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Pass _quantity to  `forceHarvest` method and update the code.


## Assessed type

Other
unlock function also uses notpaused modifier thus it can prevent the user to withdraw their tokens.
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L404


# Vulnerability details

## Impact
unlock function uses notpaused modifier which makes it too restricted as there is no trusted role so even a malicious admin can pause the contract and thus preventing the users to withdraw their tokens.

## Proof of Concept
Following is unlock function
```
function unlock(
        address _tokenContract,
        uint256 _quantity
    ) external notPaused nonReentrant {
        LockedToken storage lockedToken = lockedTokens[msg.sender][
            _tokenContract
        ];
        if (lockedToken.quantity < _quantity)
            revert InsufficientLockAmountError();
        if (lockedToken.unlockTime > uint32(block.timestamp))
            revert TokenStillLockedError();

        // force harvest to make sure that they get the schnibbles that they are entitled to
        accountManager.forceHarvest(msg.sender);

        lockedToken.quantity -= _quantity;

        // send token
        if (_tokenContract == address(0)) {
            payable(msg.sender).transfer(_quantity);
        } else {
            IERC20 token = IERC20(_tokenContract);
            token.transfer(msg.sender, _quantity);
        }

        emit Unlocked(msg.sender, _tokenContract, _quantity);
    }
```
Now it is perfectly fine to apply notPaused modifier on the lock function but incase of unlock function it can cause a scenario when a malicious admin can pause the contract and thus preventing users to get back their tokens.
This wouldn't be an issue if the admin was trusted but here there is no trusted role.

Even if the admin was trusted then also unlock function should not have notPaused function because it would prevent the users to get back their tokens even when their lock time has passed.In emergency situation the users should not be denied of their tokens. 
## Tools Used
Manual review
## Recommended Mitigation Steps
remove the notPaused modifier


## Assessed type

Context
When migrationManager calls the lock function, the user will lose the locked funds.
Audit: 2024-05-munchables-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L361-L370


# Vulnerability details

## Impact
Users will lose locked funds.

## Proof of Concept
If migrationManager calls the lock function, [the logic in the if](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L361-L370) will not be executed. Therefore, the remainder amount will still be equal to 0, and [subsequent updates to the remainder amount and quantity](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L379-L380) will cause the user to lose the previous amount of remainder. Indicates that the remainder deposited by the user cannot be divisible, and also belongs to the user's assets.

```solidity
            if (msg.sender != address(migrationManager)) {
                // calculate number of nfts
                remainder = quantity % configuredToken.nftCost;
                numberNFTs = (quantity - remainder) / configuredToken.nftCost;

                if (numberNFTs > type(uint16).max) revert TooManyNFTsError();

                // Tell nftOverlord that the player has new unopened Munchables
                nftOverlord.addReveal(_lockRecipient, uint16(numberNFTs));
            }
```

## Tools Used
manual

## Recommended Mitigation Steps
It is recommended that the remainder variable should be calculated correctly regardless of whether the addReveal function is executed or not.


## Assessed type

Other