xanoutas/solidity-security-findings huggingface dataset:
`transferToUnoccupiedPlot` can be used to "clone" and occupy plots
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L199


# Vulnerability details

## Impact
[transferToUnoccupiedPlot](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L199-L226) misses to change the old `toilerState[tokenId].plotId` to the new `plotId`, causing a mess in plots and rewards. The damage can be summarized into:
1. Landlords get their plots locked
2. Users can clear already occupied plots, bricking other users' NFTs

## Proof of Concept
[transferToUnoccupiedPlot](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L199-L226) can be used by a user to transfer his `tokenId` to another plot of the same landlord. 

However when setting the new parameters the function misses one - `toilerState[tokenId].plotId`. On a second change (B -> C), when moving A -> B -> C it won't erase `plotOccupied[landlord][B]` making plot B count as filled, even thought the NFT is now on plot C.

---

When we break the above down, for simplex understanding, we can see that when moving the NFT from plot A to B, since `toilerState[tokenId].plotId` is not set to B, it will remain as A.

Then when moving from B to C, since `toilerState[tokenId].plotId == A` the system will clear A (even thought A was already empty) and move on, without clearing plot B (as it should):

```solidity
   ToilerState memory _toiler = toilerState[tokenId];

   // plot A
   uint256 oldPlotId = _toiler.plotId; 

   ...

   // plotOccupied[landlord][A] = Plot({occupied: false, tokenId: 0});
   plotOccupied[_toiler.landlord][oldPlotId] = Plot({occupied: false, tokenId: 0});

   // plotOccupied[landlord][C] = Plot({occupied: true, tokenId: tokenId});
   plotOccupied[_toiler.landlord][plotId] = Plot({occupied: true, tokenId: tokenId});
```

This can be used by malicious users to fill the landlords plots, lowering his reward generation. It can also accidentally happen, which makes the issue even more dangerous.

Note: Another impact of this issue is that if user2 moves to the previously occupied plot, and user1 unstakes, then the unstake will clear user2 plot.


## Tools Used
Manual review

## Recommended Mitigation Steps
Make sure the `plotId` is updated to the new one:

```diff
     toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord].currentTaxRate;
+    toilerState[tokenId].plotId = plotId;

     emit FarmPlotLeave(_toiler.landlord, tokenId, oldPlotId);
     emit FarmPlotTaken(toilerState[tokenId], tokenId);
```


## Assessed type

Error
dirty flag of toilerState will not reset
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L251


# Vulnerability details

## Impact
Plots can only be formed once
## Proof of Concept
![poc](https://github.com/IIMP/submits/blob/175d4cde73e527515373437335f42fa4f8db8f8d/FF6C90E4-2422-4B4D-899F-BF4EBD4A9489.png)
## Tools Used
Manual review
## Recommended Mitigation Steps
Leave a function for resetting the flag based on the protocol design.


## Assessed type

DoS
UINT256 underflow allows to farm a large amount of Schnibbles
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L283-L286


# Vulnerability details

## Impact

Under specific conditions, the conversion to uint256 underflows, allowing a user to farm an extraordinarily large amount of Schnibbles (over 1e77 Schnibbles). These conditions are easily achievable, making the vulnerability exploitable. Exploiting this would result in a drastic inflation of Schnibbles.

## Root cause and summary 

After staking a Munchable, the user can farm a plot. The function `_farmPlots` first calculates a uint256 `schnibblesTotal`. For that it calculates a `finalBonus` value [1](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L270-L279):
```
finalBonus =
  int16(
    REALM_BONUSES[
      (uint256(immutableAttributes.realm) * 5) +
      uint256(landlordMetadata.snuggeryRealm)
    ]
  ) +
  int16(
    int8(RARITY_BONUSES[uint256(immutableAttributes.rarity)])
);
```

The `finalBonus` depends on two internal arrays with these values:
- REALM_BONUSES: [10, -10, -5, 0, 5, -10, 10, -5, 5, 0, -5, 0, 10, 5, -10, 0, -5, 10, 10, -10, 5, 10, 0, -10, 10] [2](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/deployments/utils/consts.ts#L129)
- RARITY_BONUSES: [0, 0, 10, 20, 30, 50] [3](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/deployments/utils/consts.ts#L133)

For the `REALM_BONUSES` index, the code uses the user's Munchable realm and the landlord's `snuggeryRealm`. To get a negative `finalBonus`, the user needs to pick a realm so that `index = realm * 5 + snuggeryRealm` equals one of these indexes: [1, 2, 5, 7, 10, 14, 16, 19, 23]. With those the first part of the addition will be -5 or -10. The `RARITY_BONUSES` index is based on the rarity of the user's Munchable. For the exploit the user needs to use a rarity of 0 or 1 (aka Primordial or Common). This guarantees that the second part of the addition is 0.
With those values, the `finalBonus` can either be -5 or -10, either one works to ensure an underflow will happen.

The function than calculates `schnibblesTotal` based on the time since the last toil date and the `BASE_SCHNIBBLE_RATE` [4](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L280-L282). Afterwards it adjusts that total based on the `finalBonus` [5](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L283-L286):
```
schnibblesTotal = uint256(
  (int256(schnibblesTotal) +
  (int256(schnibblesTotal) * finalBonus)) / 100
);
```
This implementation contains an error. It first calculates the bonus amount `(int256(schnibblesTotal) * finalBonus)` and then adds the total amount. With the described settings, this can be simplified t
Wrong input validation while tracking a dirty bool in _farmPlots()
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L258


# Vulnerability details

## Vulnerability details 
When _farmPlots() checks for dirty plotId, it compares landlord's number of plots to toiler.plotId.
```solidity
@> if (_getNumPlots(landlord) < _toiler.plotId) {
    timestamp = plotMetadata[landlord].lastUpdated;
    toilerState[tokenId].dirty = true;
}
```
Now the problem is, above check excludes `last` plotId which is `dirty` due to missing = sign along with <

//How this works
1. Suppose landlord has initially 4 plots ie [0, 1, 2, 3]
2. User staked for plotId = 3
3. Landlord withdraw his position, which decreased his number of plots to 3 ie [0, 1, 2]
4. Now, plotId = 3 is dirty but according to above check _getNumPlots(landlord) = 3 & plotId = 3, but 3 is not greater than 3 therefore plotId = 3 will not be marked as dirty

Note: All this happening due to missing = sign in if statement 


## Impact
Last plotId will not be marked as dirty

## Proof of Concept
https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L258C2-L261C14

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add = sign along with <
```solidity
if (_getNumPlots(landlord) <= _toiler.plotId) 
```





## Assessed type

Invalid Validation
Users won't be able to stake/unstake their tokens if they have dirty toiler younger than plotMetadata last update for landlord
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L259-L260


# Vulnerability details

## Impact
The LandManager contract allows users to stake/unstake their tokens and receive rewards based on various factors. Each time a user stakes or unstakes a token, they will first farm their rewards before proceeding with the action, due to the forceFarmPlots modifier. However, there could be a situation where a user's token toiler becomes "dirty," which will update the timestamp of the rewards calculation:

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L259-L260
```solidity
            if (_getNumPlots(landlord) < _toiler.plotId) {
                timestamp = plotMetadata[landlord].lastUpdated;
                toilerState[tokenId].dirty = true;
            }
```
Making it the last time when plotMetadata for landlord was updated. But the problem lies in the code that goes next:

https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L280-L282
```solidity
          schnibblesTotal =
                (timestamp - _toiler.lastToilDate) *
                BASE_SCHNIBBLE_RATE;
```
Here we calculate `schnibblesTotal` with the implicit assumption that `timestamp` is greater than `_toiler.lastToilDate`. However, this might not be true because we updated it from `block.timestamp` to `plotMetadata[landlord].lastUpdated`. If timestamp is lower than `_toiler.lastToilDate`, the transaction will revert, making it impossible to stake or unstake any token until you have this "dirty" token or until the AccountManager updates the `plotMetadata[landlord].lastUpdated` value.

## Proof of Concept

Let's consider this example (just to make it easier to understand let's suggest that current timestamp is 10):
1. AccountManager updates plotMetadata[landlord].lastUpdated making it 10.
2. Alice stakes her token. timestamp is 11 => _toiler.lastToilDate = 11
3. _getNumPlots changes making Alice's toiler dirty (if _farmPlots would be called). timestamp is 12
4. Alice tries to unstake her tokens, but because her token toiler is now "dirty" timestamp is updated to `plotMetadata[landlord].lastUpdated` making it 10 instead of 13. According to this formula: `schnibblesTotal = (timestamp - _toiler.lastToilDate) * BASE_SCHNIBBLE_RATE;` we would get schnibblesTotal = (10 - 11) * BASE_SCHNIBBLE_RATE and this expression will revert.

## Tools Used
Manual review.

## Recommended Mitigation Steps
Either continue loop after setting 'dirty' to true or add explicit check that timestamp is bigger than  _toiler.lastToilDate.


## Assessed type

DoS
wrong calculation of schnibblesTotal 
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L283-L286


# Vulnerability details

## Impact
https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L283-L286
here ``` 
schnibblesTotal = uint256(
                (int256(schnibblesTotal) +
                    (int256(schnibblesTotal) * finalBonus)) / 100
            );```
we are dividing sum of both schibblesTotal and (schnibblesTotal * finalBonus) by 100. which was not the correct way to calculate the schnibblesTotal as it was not only dividing (schnibblesTotal * finalBonus) by 100 as intended but dividing sum of schnibblesTotal and (schnibblesTotal * finalBonus) by 100 . Finally it leads to decreased(very much) schibblesTotal.

## Tools Used
vscode, manual review

## Recommended Mitigation
divide only (schnibblesTotal * finalBonus) by 100, not the sum of schnibblesTotal and (schnibblesTotal * finalBonus) by 100.
`schnibblesTotal = uint256(
                int256(schnibblesTotal) +
                   (int256(schnibblesTotal) * finalBonus) / 100
            );`















## Assessed type

Math
Overflow in _farmPlots leads to player not able to stake / unstake / transfer to plot any munchable
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L288


# Vulnerability details

## Impact
Particular combinations of a landlord snuggery realm, munchable rarity and munchable realm can make the function `_farmPlots` revert due to an overflow error while calculating the schnibble rewards.

Given that the function `_farmPlots` executes whenever a player wants to stake, unstake or transfer a staked munchable, those three actions will always revert for the player. Once the bug is triggered, this will happen always for that player, with any munchable and landlord he tries to use from there on, leaving any staked munchable stuck and denying the player the possibility of staking or unstaking any munchables.

Additionally, the landlord will be partially impacted because he will have at least one plot occupied that will not collect schnibbles from taxes.

## Proof of Concept
The overflow occurs due to what appears to be an incorrect way of adding the bonuses to the `schnibblesTotal` computed in the `_farmPlots` function.

In the line `283` of the file `src/managers/LandManager.sol` it looks like the intention was to increase by a percentage the value of the `schnibblesTotal` variable (or decrease if the bonus was negative):
```
            schnibblesTotal = uint256(
                (int256(schnibblesTotal) +
                    (int256(schnibblesTotal) * finalBonus)) / 100
            );
```
However, the division by `100` is not correctly applied. The division should be done on the amount `int256(schnibblesTotal) * finalBonus` , but it is actually done over `int256(schnibblesTotal) + (int256(schnibblesTotal) * finalBonus))`.

As a consequence, if the value of `finalBonus` is negative (which can happen if the realm of the landlord and the realm of the munchable are different), then the value of the expression `(int256(schnibblesTotal) + (int256(schnibblesTotal) * finalBonus)) / 100` will be negative, and when it is casted to a `uint256` the result will be a big positive number (it can be in the order of `10**69`).

In line `287`, `schnibblesTotal` is multiplied by the landlord's tax rate (greater or equal than `20 * 10**16` according to the test values), making the result of the multiplication greater or equal than `20 * 10**85`, which is more than what a `uint256` can store, causing a revert by overflow.


In the following proof of concept a player with a munchable of `Common` rarity and realm `Everfrost` is staked in a plot of a landlord with a Snuggery realm of `Drench`, this is enough for triggering the bug.

The following values are assumed in accordance to the values provided in the test suites:
```
export const REALM_BONUSES = [
  10, -10, -5, 0, 5, -10, 10, -5, 5, 0, -5, 0, 10, 5, -10, 0, -5, 10, 10, -10, 5, 10, 0, -10, 10,
];

export const RARITY_BONUSES = [0, 0, 10, 20, 30, 50];
```



You can add the following test to `./tests/managers/LandManager/stakeMunchable.test.ts` to reproduce the i
Incomplete update for `toilerState[tokenId]` to reflect plot transfer
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L199


# Vulnerability details

## Impact
In function `transferToUnoccupiedPlot()`, the `toilerState[tokenId].plotId` and `toilerState[tokenId].dirty` are not updated to reflect plot transfer. Users transfer `dirty` NFT to a new slot would become ineffective and cannot participate in farming activities.

## Proof of Concept
The `transferToUnoccupiedPlot()` function does not properly update the `toilerState[tokenId]` struct associated with the `tokenId` of the transferred NFT. The `toilerState[tokenId].plotId` is not updated to reflect the new plot ID, and `toilerState[tokenId].dirty` is not reset to `false` after the transfer.

Therefore, users cannot transfer the `dirty` NFT to a new slot to activate the farming of this NFT, as it will be skipped in [`_farmPlots()`](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L251).

## Tools Used
manual

## Recommended Mitigation Steps

```diff
         toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord]
             .currentTaxRate;
+        toilerState[tokenId].plotId = plotId;
+        toilerState[tokenId].dirty = false;
```


## Assessed type

Other
Users can avoid the `dirty` penalty 
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L258


# Vulnerability details

## Impact
When a plot can be marked as `dirty` it will pay rewards only from `toilerState[tokenId].lastToilDate` up to `plotMetadata[landlord].lastUpdated`, which may mean, that rewards are not paid up to the current `block.timestamp`.

However users can update their own metadata and claim for their `dirty` plot up to `block.timestamp`

## Proof of Concept
If a plot is to be marked as `dirty` [_farmPlots](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L258-L261) will claim the rewards only up to `plotMetadata[landlord].lastUpdated`, which can be in the past.


```solidity
    if (_getNumPlots(landlord) < _toiler.plotId) {
        timestamp = plotMetadata[landlord].lastUpdated;
        toilerState[tokenId].dirty = true;
    }

    ...

    schnibblesTotal = (timestamp - _toiler.lastToilDate) * BASE_SCHNIBBLE_RATE;
```

However users can prevent that and claim their rewards for their dirty plots by updating their landlord's `plotMetadata` using [updatePlotMetadata](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L124-L126). That can be done by locking some amount (can be 1 wei) for their landlord with [LockManager::lockOnBehalf](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LockManager.sol#L278-L294), which will trigger [AccountManager::forceHarvest](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/AccountManager.sol#L151-L155), setting their `plotMetadata[landlord].lastUpdated` to the current `block.timestamp`.

Example:
1. At T1 our user stakes at the last free slot of a landlord.
2. At T2 the landlord withdraws some of his locked tokens, decreasing his active plots.
3. At T3 our user, who's plot is less than the active plots (will be `dirty` after harvest), wants to claim his rewards.
4. He calls [lockOnBehalf](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LockManager.sol#L278-L294) for the landlord, locking 1 wei and updating his metadata.
5. Then he claims his rewards, claiming from T1 to T3.

Notice how our user has claimed rewards for time that his plot would have been considered `dirty` (T2-T3) and not paid rewards.

## Tools Used
Manual review

## Recommended Mitigation Steps
A really good solution would be to make a landlord specific map that will track the time at which plots become `dirty` and when [_farmPlots](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L258-L261) is invoked it will only query that plot and see if it's dirty or not. Example code:

```solidity
   if (_getNumPlots(landlord) < _toiler.plotId) {
      timestamp = plotDirtyTime[_toiler.plotId];
      toilerState[tokenId].dirty = true;
      plotDirtyTime[_toiler.plotId] = 0;
   }
```
The only downside to this would be that on [LockManager::unl
Users can get their NFTs completely DOSed
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L232


# Vulnerability details

## Impact
Users can be DOS from using staking or unstaking all of their NFTs.

## Proof of Concept
1. [_farmPlots](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L232) is triggered in all of the main functions - [transferToUnoccupiedPlot](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L202), [unstakeMunchable](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L175) and [stakeMunchable](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L135). If that function reverts those will revert too.

2. If a user triggers [_farmPlots](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L258-L261) and his `plotId` is bigger than the available plots, his `timestamp` (for reward math) will get set to `plotMetadata[landlord].lastUpdated`.

```solidity
   if (_getNumPlots(landlord) < _toiler.plotId) {
       timestamp = plotMetadata[landlord].lastUpdated;
       toilerState[tokenId].dirty = true;
   }

   ...

   //@audit but what if _toiler.lastToilDate > timestamp ? 
   schnibblesTotal = (timestamp - _toiler.lastToilDate) * BASE_SCHNIBBLE_RATE;
```

3. [_getNumPlots](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L344-L346) calculates the available plots using - `weight / price` formula, where the weight is calculated inside [getLockedWeightedValue](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LockManager.sol#L481-L491) to be - `quantity * price / 1e18`.
- `quantity` is the amount of tokens the landlord deposits.
- `price` is a custom price variable that is updated by oracles.


---


With the above 3 an issue can appear, where `_toiler.lastToilDate` is bigger than `timestamp`, leading to an underflow and a revert. This will cause a completed DOS for every user that has a plot, considered as dirty. This can happen with price updates, lowering the landlord's weight, causing his plots to decrease.

Example:
1. Landlord has 500 tokens, valued at 1 each, with 100 for a single plot - 5 plots total
2. User1 stakes 1 of his 10 NFTs to this landlord's last plot.
3. Oracles update the price with [proposeUSDPrice](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LockManager.sol#L142) from 1 to 0.995 (0.5% decrease), causing the landlord's weight to decrease to 497,5, marking his last plot as dirty.
4. Our user tries to unstake to be no longer on a `dirty` plot, however the TX fails as `_toiler.lastToilDate > plotMetadata[landlord].lastUpdated`, causing [timestamp - _toiler.lastToilDate](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L281) to underflow.
5. His 9 other NFTs are also stuck as [_farmPlots](https://githu