xanoutas/solidity-security-findings huggingface dataset:
Any munchNFT owner can DoS all landlord because of a missing toilerState update in transferToUnoccupiedPlot
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-L226


# Vulnerability details

## Impact
All plots of land belonging to a landlord can be DoS and made unusable by any ```munchNFT``` owner, because ```toilerState[tokenId]``` is not updated upon transferring a ```tokenId``` to an unoccupied ```plotId```.

I suggest a HIGH severity as this renders the whole staking mechanism potentially unusable for all munchNFT owners.

## Proof of Concept

Let us take a look at the [```transferToUnoccupiedPlot```](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L199-L226) function

```javascript
    function transferToUnoccupiedPlot(
        uint256 tokenId,
        uint256 plotId
    ) external override forceFarmPlots(msg.sender) notPaused {
        (address mainAccount, ) = _getMainAccountRequireRegistered(msg.sender);
        ToilerState memory _toiler = toilerState[tokenId];
        uint256 oldPlotId = _toiler.plotId;
        uint256 totalPlotsAvail = _getNumPlots(_toiler.landlord);
        if (_toiler.landlord == address(0)) revert NotStakedError();
        if (munchableOwner[tokenId] != mainAccount) revert InvalidOwnerError();
        if (plotOccupied[_toiler.landlord][plotId].occupied)
            revert OccupiedPlotError(_toiler.landlord, plotId);
        if (plotId >= totalPlotsAvail) revert PlotTooHighError();

        toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord]
            .currentTaxRate;
        plotOccupied[_toiler.landlord][oldPlotId] = Plot({
            occupied: false,
            tokenId: 0
        });
        plotOccupied[_toiler.landlord][plotId] = Plot({
            occupied: true,
            tokenId: tokenId
        });

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

In the above code, only the ```latestTaxRate``` of the ```toilerStatep[tokenId]``` is updated while the its ```plotId``` is not updated. 

Let's now take a look at the [```unstakeMunchable```](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L173-L197) function below

```javascript
    function unstakeMunchable(
        uint256 tokenId
    ) external override forceFarmPlots(msg.sender) notPaused {
        (address mainAccount, ) = _getMainAccountRequireRegistered(msg.sender);
        ToilerState memory _toiler = toilerState[tokenId];
        if (_toiler.landlord == address(0)) revert NotStakedError();
        if (munchableOwner[tokenId] != mainAccount) revert InvalidOwnerError();

        plotOccupied[_toiler.landlord][_toiler.plotId] = Plot({
            occupied: false,
            tokenId: 0
        });
        toilerState[tokenId] = ToilerState({
            lastToilDate: 0,
            plotId: 0,
            landlord: address(0),
            latestTaxRate: 0,
            dirty: false
        });
        munchableOwner[toke
Reversion in `_farmPlots()` Function Due to Incorrect `timestamp` Usage
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The tx to call `_farmPlots()` will be reverted due to the incorrect usage of `timestamp`, leading to DoS of contract and users lose their NFT.

## Proof of Concept

The usages of variable `timestamp`:

l. L248: Initiated to `block.timestamp`
2. L259: Set to `plotMetadata[landlord].lastUpdated` if `_getNumPlots(landlord) < _toiler.plotId`

However, this reassignment can lead to an issue where `timestamp` becomes smaller than `_toiler.lastToilDate`, causing the calculation at line 281 to revert.

```solidity
232:     function _farmPlots(address _sender) internal {
...
247:         for (uint8 i = 0; i < staked.length; i++) {
248:             timestamp = block.timestamp;
...
258:             if (_getNumPlots(landlord) < _toiler.plotId) {
259:                 timestamp = plotMetadata[landlord].lastUpdated;
260:                 toilerState[tokenId].dirty = true;
261:             }
...
280:             schnibblesTotal =
281:                 (timestamp - _toiler.lastToilDate) * // <= reverts
282:                 BASE_SCHNIBBLE_RATE;
```

Consider the following scenario:

1.  The `plotMetadata[landlord].lastUpdated` is set to 2024-07-01.
2.  An NFT, having been farmed earlier, has its `toilerState[tokenId].lastToilDate` set to 2024-08-01.
3.  The NFT's `plotId` becomes unfit due a reduction in locked weight.
4.  The condition `_getNumPlots(landlord) < _toiler.plotId` is true, leading to `timestamp` being set to `plotMetadata[landlord].lastUpdated` (2024-07-01).
5.  The subtraction `timestamp - _toiler.lastToilDate` becomes negative (2024-07-01 - 2024-08-01), causing the transaction to revert.

## Tools Used
manual

## Recommended Mitigation Steps

Ensure that `timestamp` is always greater than or equal to `_toiler.lastToilDate`. A check should be added to verify this condition before performing the subtraction.










## Assessed type

DoS
munchNFT can get stuck after staking.
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
https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L281
https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L163


# Vulnerability details

## Impact
The user can lock his NFT and be unable to unstake it causing him the loss of his NFT and also the loss of `schnibbles`.

## Proof of Concept
Imagine this scenario: 
1. Alice has staked ETH in the protocol , which means she has certain amount of plots, calculated by `_getNumPlots` function:
```
    function _getNumPlots(address _account) internal view returns (uint256) {
        return lockManager.getLockedWeightedValue(_account) / PRICE_PER_PLOT;
    }
```
2. Bob decides to stake his munchable NFT with the `stakeMunchable` function with landlord parameter `Alice` and plotId 0 or plotId 1, assuming Alice has enough staked to have plots.
3. Now Alice at no point in time updates her Plot metadata leaving the `lastUpdated` variable in the PlotMetadata to it's default value of 0.
4. Alice decides to unstake her tokens in the protocol, leaving her with close to 0 or funds under the `PRICE_PER_PLOT` variable , resulting in `_getNumPlots` function returning 0
5. Bob decides to unstake his munchable  NFT and `_farmPlots` function is called.
We get to this check that will be triggered because Alice has no staked funds and `_getNumPlots` returns 0:
```
            if (_getNumPlots(landlord) < _toiler.plotId) { //! What happens when a landlord removes his staked tokens?
                timestamp = plotMetadata[landlord].lastUpdated; //!lastUpdated can it be 0?
                toilerState[tokenId].dirty = true;
            }
```
`timestamp` is seto to  'lastUpdated' variable but remember this variable is 0 because Alice never updated her Plot metadata, look at point 3 above.
So timestamp being set to 0 we get to this calculation:
```
    schnibblesTotal = (timestamp - _toiler.lastToilDate) * BASE_SCHNIBBLE_RATE;
```
Where `timestamp - _toiler.lastToilDate` will be 0 - timestamp of NFT staking. 
This will return <0 and cause the function to revert.
6. Since the `_farmPlots` function reverts and it's called basically everywhere including in the `unstakeMunchable` and `transferToUnoccupiedPlot` this will leave the NFT stuck in the contract.

## Tools Used
Manual review
Integrate a check for the `lastUpdated` parameter for Plot metadata in the `_farmPlots` function.





## Assessed type

ERC721
Malicious actor can take advantage of `transferToUnoccupiedPlot` to occupy plot with almost no costs.
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Malicious actor can occupy landlord's all unoccupied plots, but only one nft is locked in LandManager.

## Proof of Concept
When user stake their Munchable on landlord's plot by call `stakeMunchable`, plot will be occupied and `munchableOwner` will be signed:
```solidity
    // plot occupied. 
    plotOccupied[landlord][plotId] = Plot({
        occupied: true,
        tokenId: tokenId
    });

    munchablesStaked[mainAccount].push(tokenId);
    // munchableOwner signed.
    munchableOwner[tokenId] = mainAccount;
```

User could call `transferToUnoccupiedPlot` to transfer ownership of plot from one to another, but the function has several problems:
1, The old plot is not checked to see if they had been occupied, even unoccupied plot could be used to occupy other unoccupied plot.
2, When transfer success, `_toiler.plotId`(old plot) is not updated to new plot.
3, After stake its munchable, munchableOwner[tokenId] is still user.

```solidity
    function transferToUnoccupiedPlot(
        uint256 tokenId,
        uint256 plotId
    ) external override forceFarmPlots(msg.sender) notPaused {
        (address mainAccount, ) = _getMainAccountRequireRegistered(msg.sender);
        ToilerState memory _toiler = toilerState[tokenId];
>       uint256 oldPlotId = _toiler.plotId;
        uint256 totalPlotsAvail = _getNumPlots(_toiler.landlord);
        if (_toiler.landlord == address(0)) revert NotStakedError();
        if (munchableOwner[tokenId] != mainAccount) revert InvalidOwnerError();
        if (plotOccupied[_toiler.landlord][plotId].occupied)
            revert OccupiedPlotError(_toiler.landlord, plotId);
        if (plotId >= totalPlotsAvail) revert PlotTooHighError();

        toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord]
            .currentTaxRate;
>       plotOccupied[_toiler.landlord][oldPlotId] = Plot({
            occupied: false,
            tokenId: 0
        });
        plotOccupied[_toiler.landlord][plotId] = Plot({
            occupied: true,
            tokenId: tokenId
        });

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

Hence, there is an exploit path:
1, Attacker call `stakeMunchable`, occupy one plot of landlord.
2, Now attacker can constantly call `transferToUnoccupiedPlot` to occupy landlord's other unoccupied plots( which plotId < totalPlotsAvail). In this step, old plot is always the plot occupied in step 1.

## Tools Used
Manual

## Recommended Mitigation Steps
When calling `transferToUnoccupiedPlot`:
1, Constrain old plot must be occupied.
2, Update `_toiler.plotId` to new plotId


## Assessed type
transferToUnoccupiedPlot did not update toileterState.plotId
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This will cause confusion in contract variable data. Land that is not dirty may eventually become dirty. User's Schnibbles will be lost.

## Proof of Concept
background:
1. The function of [`transferToUnoccupiedPlot` ](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L199)is to move an NFT from the previous plot to another plot. This is achieved by modifying `plotId`.
2. `toilerState` represents some data of a certain NFT, where `toileterState.plotId` represents which plot this NFT is currently on.
3. In the `_farmPlots` function, [whether a piece of land is dirty is determined based on the landlord's pledge. If it is dirty, Schnibbles will not be updated after this time](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L258-L261).
```solidity
            if (_getNumPlots(landlord) < _toiler.plotId) {
                timestamp = plotMetadata[landlord].lastUpdated;
                toilerState[tokenId].dirty = true;
            }
```


The update of `toileterState.plotId` is missing in the `transferToUnoccupiedPlot` function.
Both users and landowners will lose Schnibbles if:
1. User A places the NFT on landowner B’s land No. 5.
2. User A moved the NFT from location No. 5 to location No. 2.
3. Landowner B withdrew some funds, causing his land No. 5 to be dirty.

At this time, since `toileterState.plotId` is still 5, in the following judgment, this NFT will become dirty. Both users and landowners lose Schnibbles after this time.

Since this seriously affects the contract's state variable updates and easily causes capital losses, I judge this to be high.

## Tools Used
manual

## Recommended Mitigation Steps
Add the following code to the [`transferToUnoccupiedPlot` ](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L199) function.
```diff
        toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord]
            .currentTaxRate;
+       toilerState[tokenId].plotId = plotId;
```





## Assessed type

Other
When the plot is dirty, the last update time is used for settlement.
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-L261


# Vulnerability details

## Impact
Schnibbles can be defrauded from the agreement without the landlord needing to stake too much.

## Proof of Concept
This is a publicly known issue. Here is a brief explanation of how to cause harm, mainly focusing on repair suggestions. Good solutions that the client has stated will be accepted.

Users can use different addresses as landlords and NFT pledgers. The landowner first pledges a lot and opens up a lot of land, and then pledges a lot of NFT. The landowner can then take out the pledge. after a while. The landowner changes the pledge amount again (the change can be small.) The NFT staker calls `farmPlots` for settlement. At this time, settlement will be based on the latest update time of the landlord. As a result, users can obtain more land with a small pledge amount.

Due to breaking the principle of stake rewards. Similar to stealing funds from the protocol, so I judge it to be high.

## Tools Used
manual

## Recommended Mitigation Steps
The main purpose of the modification is that if the landowner reduces the mortgage, settlement should be based on the time of the first landowner renewal.

So at the end of the function where the landlord reduces the pledge, add the following logic.
If the user is reducing the pledge, use the [plotOccupied variable](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L23) to find the pieces of land that exceed the current pledge. Settle those pieces of land.






## Assessed type

Other
The transferToUnoccupiedPlot function does not consider dirty NFTs
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
If an NFT is transferred from a dirty plot to a non-dirty plot. Still can't get the Schnibbles I deserve. Users lose these Schnibbles.

## Proof of Concept
background:
1. The function of [`transferToUnoccupiedPlot` ](https://github.com/code-423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L199)is to move an NFT from the previous plot to another plot. Also, if the NFT is dirty, it will not be reset.
2. In the `_farmPlots` function, [whether a piece of land is dirty is determined based on the landlord's pledge. If it is dirty, Schnibbles will not be updated after this time](https://github.com/code- 423n4/2024-07-munchables/blob/94cf468aaabf526b7a8319f7eba34014ccebe7b9/src/managers/LandManager.sol#L258-L261).

Therefore, if an NFT is moved from a dirty plot to a non-dirty plot, it will not receive the rewards it deserves.
```solidity
    function transferToUnoccupiedPlot(
        uint256 tokenId,
        uint256 plotId
    ) external override forceFarmPlots(msg.sender) notPaused {
        (address mainAccount, ) = _getMainAccountRequireRegistered(msg.sender);
        ToilerState memory _toiler = toilerState[tokenId];
        uint256 oldPlotId = _toiler.plotId;
        uint256 totalPlotsAvail = _getNumPlots(_toiler.landlord);
        if (_toiler.landlord == address(0)) revert NotStakedError();
        if (munchableOwner[tokenId] != mainAccount) revert InvalidOwnerError();
        if (plotOccupied[_toiler.landlord][plotId].occupied)
            revert OccupiedPlotError(_toiler.landlord, plotId);
        if (plotId >= totalPlotsAvail) revert PlotTooHighError();

        toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord]
            .currentTaxRate;
        plotOccupied[_toiler.landlord][oldPlotId] = Plot({
            occupied: false,
            tokenId: 0
        });
        plotOccupied[_toiler.landlord][plotId] = Plot({
            occupied: true,
            tokenId: tokenId
        });
```

## Tools Used
manual

## Recommended Mitigation Steps

```solidity

        toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord]
            .currentTaxRate;
+       toilerState[tokenId].dirty = true;
        plotOccupied[_toiler.landlord][oldPlotId] = Plot({
            occupied: false,
            tokenId: 0
        });
        plotOccupied[_toiler.landlord][plotId] = Plot({
            occupied: true,
            tokenId: tokenId
        });
```


## Assessed type

Other
`landlord` can receive rewards without staked tokens in `lockManager`
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

## Impact
`landlord` will receive rewards even if he unstake tokens from `lockManager` and `_getNumPlots()` returns `zero` of `totalPlotsAvail` for `landlord`

## Proof of Concept
The `stakeMunchable()` function allows users to stake their MunchNFT and occupy a landlord's plot if the landlord has sufficient staked funds in the `lockManager`. 

Stake flow:
1. The user wants to stake a munchNFT and occupy a plot of a landlord.
2. The stake function checks the amount of available plot IDs of the landlord.
3. Since the amount of available slots is zero-based in the stake function, used `>=`:
    - If the landlord has `1` available slot, e.g., `_getNumPlots(landlord)` returns `1`.
    - The available plot ID is `0`; otherwise, it will revert (`if (1 >= 1) revert PlotTooHighError();`).

```solidity
    function stakeMunchable(address landlord, uint256 tokenId, uint256 plotId) external override forceFarmPlots(msg.sender) notPaused {
        //...
        uint256 totalPlotsAvail = _getNumPlots(landlord);
        if (plotId >= totalPlotsAvail) revert PlotTooHighError();
        //..
    }
```
```solidity
    function _getNumPlots(address _account) internal view returns (uint256) {
        // Calculates the number of plots based on the locked weighted value divided by the price per plot
        return lockManager.getLockedWeightedValue(_account) / PRICE_PER_PLOT;
    }
```
We understand logic of reverting (zerobased plotId).

Users can have more than one MunchNFT and occupy more than one plot across different landlords.
We assume that the user has 10 MunchNFTs, with some of them already staked.
The functions `stakeMunchable()`, `unstakeMunchable()`, or `transferToUnoccupiedPlot()` has the `forceFarmPlots()` modifier.
When a user stakes another NFT, unstakes, or transfers an NFT to a different plot, the `forceFarmPlots()` modifier calls the internal `_farmPlots()` function.
This internal function is forced to calculate all rewards for landlords and mark them as `dirty` if a landlord unstakes money from the `lockManager` and should not generate any gain since they do not stake any money. (The `_getNumPlots` function is responsible for this calculation.)

Let's simulate an attack flow:
1. A landlord stakes money for one plot, e.g., `_getNumPlots(landlord)` returns `1`.
2. A user occupy `0` plotId.
3. The landlord unstakes all or part of the staked money, and `_getNumPlots(landlord)` returns `0`.
4. The user performs any action with any of his NFTs, and the `forceFarmPlots()` modifiers force the calculation of rewards for all landlords that the user occupies with their NFT.
5. In that list, we have a malicious landlord who has unstaked his money and now has `0` numPlots.
6. `_farmPlots()` should mark this plotId as dirty, and in every subsequent calculation, it should ignore this plot, and
Munchables remain unproductive after `transferToUnoccupiedPlot` due to an uncleared 'dirty' flag, leading to loss of Schnibble generation
Audit: 2024-07-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
When a Munchable is marked as `dirty` (typically due to becoming invalid when a landlord reduces their locked amount), it stops generating Schnibbles.

`transferToUnoccupiedPlot` fails to clear the `dirty` flag when moving a Munchable to a valid plot. This results in the Munchable remaining unproductive even after being moved to a valid location, causing a loss of Schnibble generation for the user.

This directly impacts the users because:
- users lose potential Schnibble generation indefinitely, even after moving their Munchable to a valid plot.
- users may need to unstake and re-stake their Munchables to resolve the issue, incurring unnecessary gas costs.

## Proof of Concept
1. `transferToUnoccupiedPlot` moves Munchables but doesn't clear the `dirty` flag
2. Next time the user calls any of the functions that trigger `_farmPlots`, `dirty` Munchables are skipped ([link](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L251))

```javascript
for (uint8 i = 0; i < staked.length; i++) {
    _toiler = toilerState[tokenId];
    if (_toiler.dirty) continue; // Skip if the toiler state is marked as dirty.

    // ... rest of the function
}
```

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

## Tools Used

Manual review

## Recommended Mitigation Steps

Update the `transferToUnoccupiedPlot` to clear the `dirty` flag:

```diff
function transferToUnoccupiedPlot(uint256 tokenId, uint256 plotId)
    external
    override
    forceFarmPlots(msg.sender)
    notPaused
{
    // ... existing checks ...

    toilerState[tokenId].latestTaxRate = plotMetadata[_toiler.landlord].currentTaxRate;
+   toilerState[tokenId].dirty = false;  // Clear the dirty flag
    plotOccupied[_toiler.landlord][oldPlotId] = Plot({occupied: false, tokenId: 0});
    plotOccupied[_toiler.landlord][plotId] = Plot({occupied: true, tokenId: tokenId});

    // ... rest of the function ...
}
```


## Assessed type

Other