`LockManager::_lock` allows for multiple locks without reducing the token amount, enabling you to accumulate more tokens than your original holdings.
Audit: 2024-05-munchables-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

### [H-01] `LockManager::_lock` allows for multiple locks without reducing the token amount, enabling you to accumulate more tokens than your original holdings.
<br>

**Description:**

Players can secure a quantity of tokens in `LockManager::lock`, which after a certain period of time can be released via `LockManager::unlock`. However, `LockManager::_lock` does not deduct the quantity of secured tokens, allowing players to secure more tokens than they actually own.

`LockManager::lock` internally calls `LockManager::_lock`, players can choose the quantity and the tokens to be insured as long as they are `onlyActiveToken` and `onlyConfiguredToken`

```js
// [ ... ]

    LockedToken storage lockedToken = lockedTokens[_lockRecipient][
        _tokenContract
    ];

// [ ... ]

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

    lockedToken.remainder = remainder;
    lockedToken.quantity += _quantity;                             // ## [ 2 ]
    lockedToken.lastLockTime = uint32(block.timestamp);
    lockedToken.unlockTime =
        uint32(block.timestamp) +
        uint32(_lockDuration);

// [ ... ]
```

In `[ 1 ]` the quantity of tokens to be secured is sent to `address(this)`, then in `[ 2 ]` that quantity is added to the storage, as can be seen, this function does not comply with the CEI pattern.

This could allow setting `lockdrop.minLockDuration` time and securing the same `_tokenContract` many times without subtracting the `_quantity` of tokens insured and then release more tokens than you initially hold via `LockManager::unlock`.

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

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

    lockedToken.quantity -= _quantity;

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

    emit Unlocked(msg.sender, _tokenContract, _quantity);
}
```

**Impact:** Funds can be stolen by malicious players

**Proof of Concept:**

1. Calls `LockManager::setLockDuration(0)` to set `lockdrop.minLockDuration`
2. `LockManager::lock` n tokens of `onlyConfiguredToken` and `onlyActiveToken` each `lockdrop.minLockDuration` times
3. `LockManager::unlock` all