xanoutas/solidity-security-findings huggingface dataset:
`landlord` cannot earn rewards on plots marked as dirty after unstaking and restaking tokens in `lockManager` 
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
If the `landlord` unstakes their money from `lockManager` and their plots are marked as dirty, the `landlord` will not be able to receive rewards for a new plotId that becomes available when they stake money again.
A user who stakes a `MunchNFT` on the landlord's plotId could potentially never unstake it, preventing the `landlord` from receiving rewards.
In that case, the user can grief the landlord until he unstake the corresponding MunchNFT.

## Proof of Concept
- The `landlord` stakes money in `lockManager` for 10 plots, i.e. `_getNumPlots(landlord) returns 10`
- Users occupy all of the landlord's plots.
- The `landlord` unstakes part or all tokens for a short period.
- During this period, if a user performs any action with munchNFT, the internal `_farmPlots()` function marks the `landlord`'s plots as dirty.
- The `landlord` stakes part or all tokens back in `lockManager`.
- Now `_getNumPlots(landlord) returns 10` again.
- However, not all users unstake munchNFTs from the `dirty` plots of the `landlord`.
- The `landlord`'s plot IDs will remain marked as `dirty` until the user unstakes the NFT.

Dirty toilers will always be `skipped`, even if `_getNumPlots(landlord) < _toiler.plotId` is false:
```solidity
    timestamp = block.timestamp;
    tokenId = staked[i];
    _toiler = toilerState[tokenId];
@>  if (_toiler.dirty) continue;
    landlord = _toiler.landlord;
    if (_getNumPlots(landlord) < _toiler.plotId) {
        timestamp = plotMetadata[landlord].lastUpdated;
        // @audit if toiler is marked dirty it can't be marked as clean again, 
        toilerState[tokenId].dirty = true;
    }
```

The `dirty` flag changes to `false` only in `unstakeMunchable()`:

```solidity
    toilerState[tokenId] = ToilerState({
        lastToilDate: 0,
        plotId: 0,
        landlord: address(0),
        latestTaxRate: 0,
@>      dirty: false
    });
```
## Tools Used
manual review

## Recommended Mitigation Steps
Implement a system that tracks the occupied time, so when the `landlord` stakes tokens again, their occupied time begins to increase again.


## Assessed type

Other
Denial of service due to lack of a proper opt-out mechanism can result in a user's rights being violated
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact

There are two cases for this issue:

1. Inadequate handling of the exit mechanism in the `_farmPlots` function may result in scenarios where valid plots remain invalid, preventing users from earning rewards. Additionally, the landlord's plot utilization rate may be reduced by 20% or more due to the impact of currency price fluctuations.
2. The second case, while theoretically possible, is less likely to occur but can still affect users. In this situation, the landlord cannot receive rewards if the staked user does not perform any operations, and the landlord cannot do anything to trigger the rewards. Although this situation exists in theory, users will be motivated to collect rewards to end this state. However, the landlord's time will be affected as it depends on the actions of the staked user.

## Proof of Concept

For the second case, the issue is clear.

The main focus here is the first case, the key point [code snippet](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L251) in the `_farmPlots` function:

```solidity
            if (_toiler.dirty) continue;
```

```solidity
            landlord = _toiler.landlord;
            // use last updated plot metadata time if the plot id doesn't fit
            // track a dirty bool to signify this was done once
            // the edge case where this doesnt work is if the user hasnt farmed in a while and the landlord
            // updates their plots multiple times. then the last updated time will be the last time they updated their plot details
            // instead of the first
            if (_getNumPlots(landlord) < _toiler.plotId) {
                timestamp = plotMetadata[landlord].lastUpdated;
                toilerState[tokenId].dirty = true;
            }
```

Consider the following scenario:

1. A plot becomes invalid due to reduced locked funds or token price drop, and the dirty flag will be set after the user's daily farm (user behavior is uncertain, but what is clear is that users may have a need for daily harvesting that).
2. If funds are relocked or the price rises, the plot remains invalid since the dirty flag isn't reset.
3. Users must restake to earn rewards; otherwise, they receive nothing even if the plot is valid again.

For the landlord, he has to wait for other users to perform operations. Even if he wants to lock more funds to remedy the invalid plot caused by the price drop, it will be of no avail. If the staked user wants to act maliciously or doesn't notice this, it could result in a denial of service for the landlord.

Unlike the effect of calling lock/unlock directly, price changes are beyond the control of users.

As an example, ETH's price fluctuations in the last month have ranged from around [2800-3500 (refer CMC)](https://coinmarketcap.com/currencies/ethereum/), the percentage chan
Landlords can deliberately cheat in the game to earn yield via tax without effectively keeping their fund locked in the game
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The code has a logic hole which can be exploited by the cohort of landlord that they only need to lock their fund at two key timepoints and then unlock it immediately after the `minDuration` has passed --- they do not have to keeping it locked but still can achieve the same effect on the yield as if their fund has been locked for the whole time being.

These two key timepoints are:
1. At the beginning timepoint when the renters stake their Munchables
2. At the timepoint of the renters having the action of farming the plots. The landlords can use bots to listen to their renter's pending trxns of `farmPlots` or `unstakeMunchable`, and front-run it with staking funds again.

In the POC section I'll explain why this cheating behavior can achieve the same effect as if the landlord's fund had been kept locked for the whole time being. 

Because this cheating scenario works generally, so every landlord can adopt it to unfairly get more yield then they should have been entitled to, making this impact one in the large scale. Hence it is an Medium severity.

## Proof of Concept
1. Assume that initially the landlord staked their fund normally, and at `t0` a renter came to stake a Munchable at the landlord's plotId of 0;
2. After the Munchable was staked, the landlord decided that instead keeping his fund locked, he'd unlock it immediately when it's allowed to(`minDuration` passed).  Assume that he unlocked it at `t1`;
3. Later at `t2`, the renter began to submit farming actions. The landlord's bot was listening to this and immediately front-run it with staking funds again. So, at `t2` when the farming action was executed, the `_getNumPlots(landlord)` went up again, leading to these results:
    * The renter's Munchable will NOT be marked as `dirty`;
    * The renter gets full yield for the whole time being as if the landlord's plot had always been active, therefore, the landlord will get his full tax yield as well. 
4. However, we can see that for the period between `t1` and `t2` the landlord actually had no locked fund, hence shouldn't have any yield for this period. They effectively cheated to get more yield than they should've been entitled to.
5. This cheating scenario can be easily amplified in the perspectives below:
    * ALL landlords can use this strategy, and everybody can cheat to get the `t1~t2` yield. In reality, depending on the cases of the renters' activity frequency, the `t1~t2` can potentially mean a significant amount of time.
    * My illustration above is using one plot as a simplified example, but in reality this can be amplified to multiple or all plots that a certain landlord may own, depending on the scale of fund they decided to cheat upon.

For the reasons above, the impact can be amplified to a quite large scale, which makes it no longer "_an ed
User could proceed to accrue rewards even if toiler gets "dirty".
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
When landlord's plots are reduced such that staker's munchable is staked outside of the availabilty zone, staker is not affected and could proceed with the game without farming plots, not losing rewards. If Bob's plots gets increased back to the staker's availabilty zone he can farm his rewards without being penalized.

## Proof of Concept
If we have Alice as staker
Bob as landlord:

1. Bob has 15 plots to begin with.
2. Alice calls stakeMunchable(landlord = Bob, tokenId = 1, plotId = 10) and stakes on plotId 10.
3. Bob's plots get reduced to 8
4. Alice proceeds to stake on plotId:10 without any penalties because she is not calling farmPlots
5. After some time Bob's plots are increased above 10
6. Alice unstakes with all the rewards accrued for the period.
   
With this scenario it is evident that we have timespan in which Alice is accruing rewards without that being part of the game mechanic.

Likelihood = low
Impact = high
Severity = medium

## Tools Used
Manual review
## Recommended Mitigation Steps
Whenever there is an update of plot data we should check whether plots outside of the availability zone are affected and properly unstaked or toiler "dirtied".


## Assessed type

Timing
Users who know their toiler state will become dirty can trigger an update to the current timestamp to earn more
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/AccountManager.sol#L154
https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LockManager.sol#L341
https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L121
https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L259


# Vulnerability details

## Description
When a user's plot id is bigger than the max amount of plots a landlord stake can currently hold, farming at the plot will make the toiler state for a tokenId dirty, meaning the token from that point on will unrecoverably stop accruing rewards:
```solidity
function _farmPlots(address _sender) internal {
...
if (_getNumPlots(landlord) < _toiler.plotId) {
                timestamp = plotMetadata[landlord].lastUpdated;
                toilerState[tokenId].dirty = true;
            }
...
}
```

At the call in which the toiler state becomes dirty, the lastUpdated parameter is utilized as the latest timestamp to accrue the amount of schibbles to distribute:\
```solidity
schnibblesTotal =
                (timestamp - _toiler.lastToilDate) *
                BASE_SCHNIBBLE_RATE;
```

There are two ways a plotMetadata's lastUpdated parameter can get updated. The first one is the triggerPlotMetadata function, callable only by the plot owner.
The second one is the updatePlotMetadata function, callable by the AccountManager contract. This second function can be called by anyone by calling the lockOnBehalf function, at the LockManager contract. 

By locking some tokens to the land owner, a user is able to change lastUpdated to block.timestamp, here's the reason for that:
at the LockManager contract.
```solidity
function _lock(
...
accountManager.forceHarvest(_lockRecipient);
...
}
```

at the AccountManager contract.
```solidity
function forceHarvest(
        ...
        if (player.registrationDate != 0) {
            _harvest(_player);
            if (
                msg.sender == configStorage.getAddress(StorageKey.LockManager)
            ) {
                landManager.updatePlotMetadata(_player);
            }
        }
    }
```

Since locking tokens on behalf of someone updates the target's plot metadata, anyone can call this function to keep the lastUpdated value as close as possible to block.timestamp.

Now, if a user notices his toiler state is about to become dirty, he/she just has to lock a small amount of tokens on behalf of the landlord and get the most amount of schnibbles as possible. 
## Impact
Users who know about this specific sequence of actions may earn more rewards than the users who don't.
The rewards distribution mechanism unfairly distributes more rewards to users who know about the exploit.
## Tools Used
Manual review
## Recommended Mitigation Steps
Make sure farm actions update the plot's lastUpdated value to block.timestamp, as this diminishes the opportunity for users to earn different rewards for equal s
User can go unscathed and farm all points as if his plot was valid all the time - while the plot was invalid due to unlocking by landlord
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Summary
Users can farm on invalid plots as long as they stake and unstake in times when their plot id is valid. In the meantime landlord can unlock funds - and make the plot invalid.

## Impact
Users who staked their Munchable on a plot that is no longer valid at a later point in time can still collect those points farmed as if they have always been valid if the landlord locks some funds later on and makes the plot valid again.
This is enabled because the plot validity will be checked only twice - when staking and unstaking (during farming). Landlord can unlock funds in the meantime.

This opens incentive for landlords and users to collude in "cheating" the protocol. Landlord offers minimum tax rate in order for the users to stake on their land. Landlords then unlock their funds while users will not lose farmed points as they wait for the funds to be locked again.

Therefore - their plot status and farmed points must be updated as soon as their plots are not valid anymore.

Since landlords can already lock their funds - for some of them the 30 day waiting period to unlock - has already finished.

## Proof of Concept
1. Users stakes his Munchable while there are 5 plots available on plot id 4.
2. Landlord unlocks his funds - making only 3 of his plots available - and this fact is NOT stored or updated on the contract
3. Time passes
4. Landlord locks his funds again, making the invalid plot available again.
5. User hasn't farmed since staking, he can farm points as if his plot never was unavailable - because plot validity is only assessed during farming (see links to affected code)


## Tools Used
Manual review

## Recommended Mitigation Steps
Here we will describe a potential solution to this problem. It requires a somewhat change in the design of the contract.

First we introduce one new field to both ```ToilerState``` and ```PlotMetadata``` struct. 
```
    struct ToilerState {
...
        uint32 wentOutOfRange;
    }
```
```
    struct PlotMetadata {
...
        uint256 totalPlots;
    }
```

```wentOutOfRange``` is a timestamp that marks the time the plot was marked invalid and user can no longer farm on it. ```totalPlots``` keeps track of the total valid plots a landlord has. We need to have it stored because we are going to update user plots when there is a decrease in landlord valid plots.

Our main improvement is that plots are invalidated at the exact moment they become so - when a landlord is unlocking funds. So ```updatePlotMetadata(address)``` function has to be updated to reflect that:
This goes in the else clause in the updatePlotMetadata function:
```
            uint256 oldTotalPlots = plotMetadata[landlord].totalPlots;
            uint256 newTotalPlots = _getNumPlots(landlord);
            if (newTotalPlots < oldTotalPlots) {
                for (uint256 i =
Incorrect Timestamp Update for Dirty Plots
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details



## Summary
The _farmPlots() function incorrectly updates the timestamp for "dirty" plots, potentially leading to significant underestimation of rewards for affected Munchables.

## Vulnerability Details
In the _farmPlots() function:

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

When a plot is marked as "dirty" (i.e., the plot ID no longer fits the landlord's available plots), the function uses plotMetadata[landlord].lastUpdated as the timestamp for reward calculations. This value could be much more recent than the actual last farming time for the Munchable.

## Impact

- Users with Munchables on "dirty" plots will receive significantly reduced rewards.
- The longer a user goes without farming, the more severe the impact becomes.
- This could lead to substantial economic losses for affected users and disrupt the game's reward distribution mechanics.

## Tools Used
- Manual code review


## Recommendations
1. Store the original staking timestamp for each Munchable:
   ```solidity
   struct ToilerState {
       uint256 originalStakeTimestamp;
       // other fields...
   }
   ```
2. Use the stored original timestamp for reward calculations:
   ```solidity
   uint256 rewardPeriod = timestamp - _toiler.originalStakeTimestamp;
   ```
3. Implement a mechanism to properly migrate "dirty" plots to new valid plots without losing accumulated reward time.
4. Add events to notify users when their plots become "dirty" so they can take action.


## Assessed type

Error
Permanent Loss of Rewards for "Dirty" Plots in _farmPlots()
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details



## Summary
The _farmPlots() function permanently skips processing of Munchables on "dirty" plots, leading to a permanent loss of rewards for affected assets.

## Vulnerability Details
In the _farmPlots() function:

```solidity
if (_toiler.dirty) continue;
```

Once a plot is marked as "dirty", the function skips it in all future farming operations without updating its state or allowing for recovery.

## Impact
- Severity: Critical
- Munchables on "dirty" plots permanently stop generating rewards.
- Users could suffer significant economic losses if their valuable Munchables become stuck in this state.
- This could lead to frustration and loss of trust in the game mechanics, potentially driving users away.

## Tools Used
- Manual code review


## Recommendations
1. Implement a recovery mechanism for "dirty" plots:
   ```solidity
   if (_toiler.dirty) {
       if (canReassignPlot(_toiler)) {
           reassignPlot(_toiler);
       } else {
           // Implement fallback reward calculation or alert user
       }
   }
   ```
2. Create a function allowing users to manually "rescue" Munchables from dirty plots.



## Assessed type

Context
`lastUpdated` is not updated in `updateTaxRate`
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The `plotMetadata[landlord].lastUpdated` field is not updated in the `updateTaxRate` function, leading to inconsistencies in the use of the `lastUpdated` field. This can result in incorrect tax calculations, as the timestamp remains unchanged from its previous value.

## Proof of Concept

In the `LandManager` contract, users can call the updateTaxRate function to update the `currentTaxRate`. However, the `plotMetadata[landlord].lastUpdated` field is not updated when this function is called.

```solidity
    function updateTaxRate(uint256 newTaxRate) external override notPaused {
        (address landlord, ) = _getMainAccountRequireRegistered(msg.sender);
        if (newTaxRate < MIN_TAX_RATE || newTaxRate > MAX_TAX_RATE)
            revert InvalidTaxRateError();
 @=》   if (plotMetadata[landlord].lastUpdated == 0)
            revert PlotMetadataNotUpdatedError();
        uint256 oldTaxRate = plotMetadata[landlord].currentTaxRate;
        plotMetadata[landlord].currentTaxRate = newTaxRate; // @note chaning currentTaxRate half-way would cause issues.
        emit TaxRateChanged(landlord, oldTaxRate, newTaxRate);
    }
```

This is inconsistent with the usage of `lastUpdated` field. Since every time the meta-info is updated, the `lastUpdated` field should be changed accordingly.

Since the `lastUpdated` field is used later in `_farmPlots` to calculate the tax, failure to update it in `updateTaxRate` can lead to incorrect calculations.

```solidity
            if (_getNumPlots(landlord) < _toiler.plotId) {
@=>             timestamp = plotMetadata[landlord].lastUpdated;
                toilerState[tokenId].dirty = true;
            }
            ...
            schnibblesTotal =
                (timestamp - _toiler.lastToilDate) *
                BASE_SCHNIBBLE_RATE;
```

## Tools Used

Manual

## Recommended Mitigation Steps

To mitigate this issue, it is recommended to update the `lastUpdated` field in the function `updateTaxRate`.


## Assessed type

Timing