xanoutas/solidity-security-findings huggingface dataset:
LockManager does not handle Rebasing tokens properly
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This can lead to instances where are funds getting stuck and some players will not be able to withdraw sometimes.

Per the [audit documentation](https://code4rena.com/audits/2024-05-munchables#toc-3-erc20-token-behaviors-in-scope) rebasing tokens are in the scope of this audit.



## Proof of Concept

When players lock their tokens with `lock(...)` function the player’s `lockedToken.quantity` is updated to include the amount tokens that the player entered.

However, if the the `tokenContract` is a rebasing token the players `lockedToken.quantity` will be over inflated when the token supply reduces.

```solidity
File: LockManager.sol
373:         // Transfer erc tokens
374:         if (_tokenContract != address(0)) { // normal ERC not ETH
375:             IERC20 token = IERC20(_tokenContract);
376:             token.transferFrom(_tokenOwner, address(this), _quantity);
377:         }
...
379:         lockedToken.remainder = remainder;
380:         lockedToken.quantity += _quantity;

```

- Alice locks 20_000e18 token A which is a rebase token
- Alice `lockedToken.quantity` = 20_000e18
- 20_000e18 is transferred from Alice wallet to `LockManager` but rebase applies and balance of `LockManager` decreases
- Alice tries to `unlock(...)` but her tokens are stuck because there is not enough funds for her to withdraw and she can only withdraw exactly `lockedToken.quantity`.

## Tools Used
Manual review

## Recommended Mitigation Steps
- Modify the `_lock(...)` such that the balance `LockManager` is checked  before and after the transfer is made from the player.
- unlock should be done according to the percentage of the player share in the `LockManager` balance


## Assessed type

Token-Transfer
Rebasing rewards will get stuck on the contract forever or users may be unable to withdraw any tokens if rebasing tokens are deposited.
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The protocol supports rebasing token behavior in scope but the ``LockManager.sol`` contract doesn't implement any functionality for handling rebasing tokens. This leads to rebasing rewards getting stuck on the contract forever or  users may be unable to withdraw any tokens if rebasing tokens are deposited.

## Proof of Concept
The contest [readme](https://github.com/code-423n4/2024-05-munchables/blob/main/README.md#erc20-token-behaviors-in-scope) clearly mentions ``Balance changes outside of transfers`` behaviour is in scope. Thus, rebasing/airdrop tokens are supported by the protocol.

When the ``lock()`` function is called to lock tokens inside the contract, the balance is cached as it is.
```solidity
    function _lock(
        address _tokenContract,
        uint256 _quantity,
        address _tokenOwner,
        address _lockRecipient
    ) private {
        ...

        // Transfer erc tokens
        if (_tokenContract != address(0)) {
            IERC20 token = IERC20(_tokenContract);
            token.transferFrom(_tokenOwner, address(this), _quantity);
        }

        ...

        lockedToken.quantity += _quantity;
```
In case of rebasing(token balance goes up/down without transfers), It is possible that the contract balances decreases over time but user can withdraw their deposit balances using the ``unlock()`` function without accounting for the decreased amount. This results in other users funds getting cut off or user being unable to withdraw any amount.
```solidity
    function unlock(address _tokenContract, uint256 _quantity) external notPaused nonReentrant {
        LockedToken storage lockedToken = lockedTokens[msg.sender][_tokenContract];
        if (lockedToken.quantity < _quantity) {
            revert InsufficientLockAmountError();
        }
        ...

        lockedToken.quantity -= _quantity;

        ...

        } else {
            IERC20 token = IERC20(_tokenContract);
            token.transfer(msg.sender, _quantity);
        }
        ...

    }
```
It is also possible that the balances will increase over time but the users can only withdraw the deposit amount and the rest of the increased balance will be stuck in the contract forever as there is no way to ``recover`` those increased amounts.

## Tools Used
Manual Analysis

## Recommended Mitigation Steps
For rebasing tokens, when they go down in value, there should be a method to update the cached reserves accordingly, based on the balance held. This is a complex solution. Also, when they go up in value, there should be a method to actually transfer the excess tokens out of the protocol (possibly directly to users).


## Assessed type

Token-Transfer
Contract does not account for fee on transfer 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#L311-L398


# Vulnerability details

## Impact
The function `_lock` does not account for tokens that have a fee on transfer which results in the contract having less funds than perceived and the user having a bigger `quantity` deposited than they have which can result in the contract being drained.

## Proof of Concept
Alice wants to lock 10 tokens which have a 10% fee on transfer. She calls `lock()` and reaches the point in the code where her tokens are taken: 
       `if (_tokenContract != address(0)) {
            IERC20 token = IERC20(_tokenContract);
            token.transferFrom(_tokenOwner, address(this), _quantity);
        }
        ...
        lockedToken.quantity += _quantity;
`
The contract calls `transferFrom` with the value 10 because that is what Alice sent. But since the token has a 10% transfer fee the contract will receive 9 tokens but Alice will be registered as having 10 tokens.

After when Alice calls `unlock` to get her tokens back with the value 10, the contract will approve the call since Alice supposedly has 10 tokens. Then the function `transfer` will be called again with the value 10. Alice will receive 9 tokens because of the fee but the `LockManager` contract will have lost 1 token through the whole process.

Contract initial balance - 10 tokens
Contract balance after Alice called `lock` - 19 tokens
Contract balance after Alice called `unlock` - 9 tokens


## Tools Used
Manual review

## Recommended Mitigation Steps
Check the actual received tokens by saving balanceOf(address.this) before and after transferring the tokens and updating `quantity` with that value.


## Assessed type

ERC20
Players can send funds using the lock method even outside the lockdrop timeframe
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
According to Documentation, 

```
the lockdrop period refers to a specific timeframe 
during which participants can lock up their tokens 
to receive rewards and benefits.

[...]

2. Locking Tokens During the Lockdrop: When tokens 
are locked using the lock function, the contract 
checks if the current timestamp is within the lockdrop 
period defined by the start and end times. If it is, 
and the lock duration meets the specified minimum and 
maximum durations, the tokens are successfully locked.

[...]

This mechanism allows the LockManager to handle token 
locks securely and according to the predefined rules 
of the lockdrop period, ensuring that participants can 
only lock their tokens during the specified timeframe 
and under the correct conditions.
```

However, outside the lockdrop timeframe, is still possible to lock tokens without receiving any NFT. This leads to loss of funds by players who are not aware that the lockdrop timeframe is closed.

## Proof of Concept
The [_lock() method](https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L311-L398) can be called through the [lock() method](https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L296-L309) by a player who would lock his/her funds and mint munchables.

This function can be called, according to documentation, only during the lockdrop timeframe. To assert this, there is a check inside [LockManager.sol#L352-L355](https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L352-L355):

```
LockManager.sol#L352-L355

352      if (
353        lockdrop.start <= uint32(block.timestamp) &&
354        lockdrop.end >= uint32(block.timestamp)
355      ) {
```

However, if this check is not satisfied, the function doesn't revert: it continues with lines [LockManager.sol#L373-L397](https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L373-L397). In particular, it executes lines [LockManager.sol#L373-L377](https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L373-L377):

```
LockManager.sol#L373-L377

373      // Transfer erc tokens
374      if (_tokenContract != address(0)) {
375        IERC20 token = IERC20(_tokenContract);
376        token.transferFrom(_tokenOwner, address(this), _quantity);
377      }
```

i.e., it transfers token from the player to this contract (or eth, because the `lock()` method is `payable`).

Even if the issue is due to a wrong behavior of the player, the documentation states that this behavior shouldn't be possible. Furthermore, developers disabled the `receive()` method ([LockManager.sol#L93-L95](https://github.com/code-423n4/2024-05-munchables/blob/main/
A user cant withdraw the remainder
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

When a user unlocks tokens, they only receive the quantity they requested, even if it means leaving a remainder in the contract. If the user wants to withdraw the remaining tokens, they must lock tokens again and wait at least 30 days before they can unlock and withdraw the remainder. This process is required solely for withdrawing the leftover tokens.

## Proof of Concept

## Tools Used

Manual review

## Recommended Mitigation Steps

Add a method for the user to be able to withdraw the remainder


## Assessed type

Error
Unsafe downcasting of _duration leads to precision loss
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The value of _duration is used to update the lockDuration of a player.
Before being pushed to the playerSettings, it is checked that it is less than the MaxLockDuration.
The comparison is done in uint256 for _duration and StorageKey.MaxLockDuration which is set in uint32 but upcast to uint256 via configStorage.getUnit().

_duration is then downcast to uint32 which creates the issue with the following scenarios possible as a result.

Scenario 1: Given a user passed the MaxLockDuration check and has no existing locked tokens, the execution of setLockDuration updates their settings and ends. If  the downcasted _duration value is 0 or less than lockdrop.minLockDuration it will be set to minLockDuration which may not be the intention of the user. Limiting their return for locking tokens

Scenario 2: Given a user passed the MaxLockDuration check and has locked tokens, the downcasted _duration value can create a DOS where the block.timestamp + _duration is less than the current unlockTime which was may not be the intention of the user

## Proof of Concept
Scenario 1 Code Snippet: https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L347-L360

Scenario 2 Code Snippet: https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L249-L261

## Tools Used
Manual Review

## Recommended Mitigation Steps
Accept uint32 for _duration and perform MaxLockDuration comparison in uint32 instead of uint256


## Assessed type

Under/Overflow
LockManager does not handle Fee on Transfer tokens properly
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This can lead to funds getting stuck and some players will not be able to withdraw

## Proof of Concept
From the [audit documentation](https://code4rena.com/audits/2024-05-munchables#toc-3-erc20-token-behaviors-in-scope) Fee on transfer tokens are in the scope of this audit.


When players lock their tokens with `lock(...)` function the player’s `lockedToken.quantity` is updated to include the amount tokens that the player entered.

However, if the the `tokenContract` is a Fee on Transfer token the players `lockedToken.quantity` will be over inflated

```solidity
File: LockManager.sol
373:         // Transfer erc tokens
374:         if (_tokenContract != address(0)) { // normal ERC not ETH
375:             IERC20 token = IERC20(_tokenContract);
376:             token.transferFrom(_tokenOwner, address(this), _quantity);
377:         }
...
379:         lockedToken.remainder = remainder;
380:         lockedToken.quantity += _quantity;

```

- Alice locks 20_000e18 token A which has a 1% fee on transfers
- Alice `lockedToken.quantity` = 20_000e18
- 20_000e18 is transfered from Alice wallet to `LockManager` but 1% fee applies and balance of `LockManager` = 19_800e18
- Alice tries to `unlock(...)` but her tokens are stuck because there is not enough funds for her to withdraw and she can only withdraw exactly `lockedToken.quantity`

## Tools Used
Manual review

## Recommended Mitigation Steps
Modify the `_lock(...)` such that the balance `LockManager` is checked  before and after the transfer is made from the user and use the difference to update the `lockedToken.quantity`  and even the `numberNFTs` variables respectively for the player.

As shown below:

```solidity
312:     function lock(
313:         address _tokenContract,
314:         uint256 _quantity
315:     )
316:         external
317:         payable
318:         notPaused
319:         onlyActiveToken(_tokenContract)
320:         onlyConfiguredToken(_tokenContract)
321:         nonReentrant
322:     {
323:         _lock(_tokenContract, _quantity, msg.sender, msg.sender);
324:     }
325: 
326:     function _lock(
327:         address _tokenContract,
328:         uint256 _quantity,
329:         address _tokenOwner,
330:         address _lockRecipient
331:     ) private {
332:         (
333:             address _mainAccount,
334:             MunchablesCommonLib.Player memory _player // registrationDate, lastPetMunchable, lastHarvestDate, Realm, maxSnuggerySize, unfedSchnibbles, referrer
335:         ) = accountManager.getPlayer(_lockRecipient);
336:         if (_mainAccount != _lockRecipient) revert SubAccountCannotLockError();
337:         if (_player.registrationDate == 0) revert AccountNotRegisteredError();
338:         // check approvals and value of tx matches
339:         if (_tokenContract == address(0))
The entire locking/unlocking functionality can be DOS'ed.
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The entire locking/unlocking functionality can be DOS'ed if tokens with >18 decimals are used.

## Proof of Concept
The contest [Readme](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L461) clearly mentions ``High decimals (>18)`` being in scope.

But, if tokens with ``>18`` decimals are configured, The `` getLockedWeightedValue()`` function will revert [here](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L473-L475) due to underflow.

```solidity
getLockedWeightedValue():

                uint256 deltaDecimal = 10 ** (18 - configuredTokens[configuredTokenContracts[i]].decimals);

```

``getLockedWeightedValue()`` function is used by ``forceHarvest()`` function to get daily schnibbles.
```solidity

 function getDailySchnibbles(
        address _caller
    ) public view returns (uint256 _dailySchnibbles, uint256 _bonus) {
        uint256 weightedValue = lockManager.getLockedWeightedValue(_caller);
        // Arbitrary division here... If we remove it, we just need to make sure we modify level thresholds, & social/pet bonuses
        _dailySchnibbles = (weightedValue / 10);
        _bonus = bonusManager.getHarvestBonus(_caller);
    }

    function _harvest(address _caller) private returns (uint256 _harvested) {
        (uint256 dailySchnibbles, uint256 bonus) = getDailySchnibbles(_caller);
...
```
 and ``forceHarvest()`` functionality is used by both ``lock()`` and ``unlock()`` functions. Thus, both locking and unlocking functionality will revert if tokens with ``> 18`` decimals are configured.

```solidity
 function _lock(address _tokenContract, uint256 _quantity, address _tokenOwner, address _lockRecipient) private {
       ...

        // they will receive schnibbles at the new rate since last harvest if not for force harvest
        accountManager.forceHarvest(_lockRecipient);
       ...
```
```solidity
    function unlock(address _tokenContract, uint256 _quantity) external notPaused nonReentrant {
       ...
        // force harvest to make sure that they get the schnibbles that they are entitled to
        accountManager.forceHarvest(msg.sender);
       ...
```


## Tools Used
Manual Analysis

## Recommended Mitigation Steps
Modify the ``getLockedWeightedValue()`` function to handle decimals more than 18.


## Assessed type

Decimal
Tokens with more than 18 decimal points will cause issues
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Tokens with more than 18 decimal points will cause issues

## Proof of Concept
It is assumed that the maximum number of decimals for each token is 18.

However uncommon, it is possible to have tokens with more than 18 decimals, as an example YAMv2 has 24 decimals.

This can result in broken code flow and unpredictable outcomes (e.g. an underflow will result with really high rates).

```solidity
LockManager.sol#L473-L475
 uint256 deltaDecimal = 10 **
                    (18 -
                        configuredTokens[configuredTokenContracts[i]].decimals);
```

## Tools Used
manual

## Recommended Mitigation Steps
Make sure the code won’t fail in case the token’s decimals is more than 18.


## Assessed type

Decimal
Incorrect decimals configuration can lead to breaking external functionality
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
```LockManager::getLockedWeightedValue``` contains a ```deltaDecimal``` calculation using the decimals set for the configured token, in this calculation the decimals being higher than 18 will result in an underflow error.

The ```AccountManager::getDailySchnibbles``` and ```BonusManager::getHarvestBonus``` contain functionality that depend on calling the ```LockManager::getLockedWeightedValue``` function, which by reverting renders those functions useless.

## Proof of Concept
We can modify the existing test file ```getHarvestBonus.test.ts``` and change line [79](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/tests/managers/BonusManager/getHarvestBonus.test.ts#L79) from ```decimals: 18,``` to ```decimals: 19,```.

After implementing that change we run ```npm run test:typescript``` which when getting to that test will run into the following error:
```
test at file:/home/jesus/Projects/audits/2024-05-munchables/tests/managers/BonusManager/getHarvestBonus.test.ts:1:14763
✖ should return combined max bonus (74.709468ms)
  Error [ContractFunctionExecutionError]: The contract function "getHarvestBonus" reverted with the following reason:
  Arithmetic operation resulted in underflow or overflow.
```

## Tools Used
Manual review + node tests

## Recommended Mitigation Steps
During the token configuration in ```LockManager::configureToken``` add a validation for the maximum number of decimals.


## Assessed type

Math
Number of NFTs can overflow
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
If a user owns enough NFTs and wants to increase his lock, his NFTs will cause an overflow on uint16

## Proof of Concept
When a user calls `lock()` during a Lockdrop event, all of his unused funds are taken in order to calculate `numberNFTs` and ensure that it is not more than what uint16 can handle. But the check is not enough because the number of his NFTs can overflow because of numerous lock increases and not one big lock.

Example:
If `numberNFTs` would overflow if the user deposits e.g. `quantity = 15000`. Alice is allowed to make 2 different deposits of `_quantity = 7500` and not get reverted but her NFTs would have actually overflowed. 

## Tools Used
Manual review

## Recommended Mitigation Steps
Add a check that a user's total NFTs do not exceed `type(uint16).max` not only the current deposit.


## Assessed type

Under/Overflow
Users will lose their remainder
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Users will lose their remainder assets.

## Proof of Concept
[Remainder represents the remainder of the user's deposited assets that cannot be divisible by nftcost](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L363). This part is also the user's asset. However, [in the unlock function](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L401), users cannot withdraw this part of the assets. They will lose these assets.

## Tools Used
manual

## Recommended Mitigation Steps
It is recommended to add the user's remainder when checking the number of user unlocks.


## Assessed type

Math
Lack of _quantity Check in lock and lockOnBehalf Functions.
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


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps


## Assessed type

Invalid Validation