xanoutas/solidity-security-findings huggingface dataset:
Major insolvency risk in `LiquidationLogic::executeCrossLiquidateERC721()` due to not setting a maximum liquidation price
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/main/src/libraries/logic/LiquidationLogic.sol#L500


# Vulnerability details

## Impact
Whenever the price of a nft suffers a significant drop or the debt asset increases in price, there is a threshold after which liquidations are not profitable at all for the liquidator. Moreover, it does not offer the possibility of partial liquidation to at least partially mitigate this issue.

## Proof of Concept

In `LiquidationLogic::executeCrossLiquidateERC721()`, the actual debt to liquidate is calculated in [LiquidationLogic::_calculateDebtAmountFromERC721Collateral()](https://github.com/code-423n4/2024-07-benddao/blob/main/src/libraries/logic/LiquidationLogic.sol#L202). [Here](https://github.com/code-423n4/2024-07-benddao/blob/main/src/libraries/logic/LiquidationLogic.sol#L500), it calculates the price of the liquidated nft by dividing the debt by the number of collateral tokens the user has. The problem with this approach is that it places no upper limit on the price, such that if the debt increases to much or the nft price suffers a sudden loss, the price of each nft would be too high. The amount of debt to repay is calculated based on this price, so it would make no sense for liquidators to pay an inflated amount for each nft.

Additionally, it's possible to liquidate only a selected amount of nfts at a time by setting `params.collateralTokenIds.length` to less than the nft balance of the borrower, but it always sells them to the liquidator at the same inflated price. Thus, it's impossible to use this mechanism to repay some amount of debt at a profitable price for the liquidator.

Place the following test in `TestIntCrossLiquidateERC721.t.sol`, confirming that the liquidator takes a big loss if the price of the nft collateral drops significantly.
```solidity
function test_POC_RepayUSDT_HasBAYC_InflatedPrice() public {
  prepareUSDT(tsDepositor1);

  uint256[] memory depTokenIds = prepareCrossBAYC(tsBorrower1);

  TestUserAccountData memory accountDataBeforeBorrow = getUserAccountData(address(tsBorrower1), tsCommonPoolId);

  // borrow some eth
  uint8[] memory borrowGroups = new uint8[](1);
  borrowGroups[0] = tsLowRateGroupId;

  uint256[] memory borrowAmounts = new uint256[](1);
  borrowAmounts[0] =
    (accountDataBeforeBorrow.availableBorrowInBase * (10 ** tsUSDT.decimals())) /
    tsPriceOracle.getAssetPrice(address(tsUSDT));

  actionCrossBorrowERC20(
    address(tsBorrower1),
    tsCommonPoolId,
    address(tsUSDT),
    borrowGroups,
    borrowAmounts,
    new bytes(0)
  );

  // make some interest
  advanceTimes(365 days);

  // drop down price and lower heath factor
  uint256 baycCurPrice = tsBendNFTOracle.getAssetPrice(address(tsBAYC));
  uint256 baycNewPrice = (baycCurPrice * 60) / 100;
  tsBendNFTOracle.setAssetPrice(address(tsBAYC), baycNewPrice);

  TestUserAccountData memory accountDataAfterBorrow = getUserAccountData(address(tsBorrower1), tsCommonPoolId);
  assertLt(
In YieldStakingBase.sol, users can avoid paying unstakeFine by front-running botAdmin's unstake call
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/yield/YieldStakingBase.sol#L329-L336


# Vulnerability details

## Impact
In YieldStakingBase.sol, users can avoid paying unstakeFine by front-running botAdmin's`unstake` call.

## Proof of Concept
When a user's healthFactor exceeds the threshold (user's staking position is unsafe), a botAdmin can call `unstake()` which will charge an unstakeFine that the user has to pay during repay.

However, current implementation in `_unstake()` is vulernable to user front-run.
Suppose a botAdmin calls unstake() for a user's nft staking position that is unhealthy. The intended behavior is `if (msg.sender == botAdmim) {` branch will execute which will add botAdmin input `unstakeFine` to user's position `sd.unstakeFine = unstakeFine`.

But the user can easily front-run botAdmin's call with unstake call. In the front-running call, `if (msg.sender == botAdmim) {` branch will be bypassed and health factor will not be checked, no fines will be added, and `sd.state` is set to  YIELD_STATUS_UNSTAKE. 
```solidity
//src/yield/YieldStakingBase.sol
  function _unstake(uint32 poolId, address nft, uint256 tokenId, uint256 unstakeFine) internal virtual {
...
    // check the nft ownership
    (vars.nftOwner, vars.nftSupplyMode, vars.nftLockerAddr) = poolYield.getERC721TokenData(poolId, nft, tokenId);
    require(vars.nftOwner == msg.sender || botAdmin == msg.sender, Errors.INVALID_CALLER);
...
    // sender must be bot or nft owner
     //@audit this if branch will only run when caller is botAdmin, and when user's position is unhealthy (hf < nc.unstakeHeathFactor)
|>   if (msg.sender == botAdmin) {
      require(unstakeFine <= nc.maxUnstakeFine, Errors.YIELD_ETH_EXCEED_MAX_FINE);

      uint256 hf = calculateHealthFactor(nft, nc, sd);
      require(hf < nc.unstakeHeathFactor, Errors.YIELD_ETH_HEATH_FACTOR_TOO_HIGH);

 |>   sd.unstakeFine = unstakeFine;
      totalUnstakeFine += unstakeFine;
    }
    sd.state = Constants.YIELD_STATUS_UNSTAKE;
    sd.withdrawAmount = convertToYieldAssets(address(vars.yieldAccout), sd.yieldShare);
    accountYieldInWithdraws[address(vars.yieldAccout)] += sd.withdrawAmount;

    protocolRequestWithdrawal(sd);

    // update shares
    accountYieldShares[address(vars.yieldAccout)] -= sd.yieldShare;
    sd.yieldShare = 0;

    emit Unstake(msg.sender, nft, tokenId, sd.withdrawAmount);
  }
```
(https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/yield/YieldStakingBase.sol#L329-L336)

User can then call [repay()](https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/yield/YieldStakingBase.sol#L391) to close the position without fines.

## Tools Used
Manual

## Recommended Mitigation Steps
(1) Consider also checking health factor when msg.sender is nftOwner; and (2) if the health factor drops below the threshold, either revert (only allowing botAdmin to
yieldGroup borrow is mixed with cross borrow when calculating crossBorrow healthFactor, risk of users being unfairly liquidated
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/GenericLogic.sol#L150


# Vulnerability details

## Impact
yieldGroup borrow is mixed with cross borrow when calculating crossBorrow healthFactor, risk of users being unfairly liquidated.

## Proof of Concept
When liquidating a cross-borrow loan(LiquidateLogic::executeCrossLiquidateERC20), `GenericLogic.calculateUserAccountData` will be invoked to return the borrower's healthFactor. 

The problem is GenericLogic.calculateUserAccountData's implementation will also add the user's yieldGroup borrow in the combined debt value (`vars.userAssetDebtInBaseCurrency`). This inflates the cross-borrow debt value.

Note that yieldBorrow is borrowed against an isolate-mode nft token. And the leverageFactor and liquidation threshold of a yieldBorrow is evaluated separately based on the single nft token. This means yieldBorrow debts have no bearings on any regular cross-borrow collaterals (ERC20 and cross-mode supplied nft tokens).

Currently, yieldBorrow debt is stored in crossBorrow accounting for the yield group (`groupData.userScaledCrossBorrow[userAccount]`). See [YieldLogic::executeYieldBorrowERC20](https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/YieldLogic.sol#L67) -> [VaultLogic.erc20IncreaseCrossBorrow(groupData, vars.stakerAddr, params.amount)](https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/VaultLogic.sol#L380).

As we can see, when determining liquidation for cross-borrow loans, yieldBorrow debt group (yieldGroupId = 0) shouldn't be accounted in the health factor implementation. But we see in GenericLogic::calculateUserAccountData, usersDebt in all asset groups (including groupId 0 / yieldGroup) is added together. Note: `currentAssetData.groupList.values()` include all groups including groupdId 0.
```solidity
//src/libraries/logic/GenericLogic.sol
  function calculateUserAccountData(
    DataTypes.PoolData storage poolData,
    address userAccount,
    address collateralAsset,
    address oracle
  ) internal view returns (ResultTypes.UserAccountResult memory result) {
...
    vars.userBorrowedAssets = VaultLogic.accountGetBorrowedAssets(accountData);
    for (vars.assetIndex = 0; vars.assetIndex < vars.userBorrowedAssets.length; vars.assetIndex++) {
      vars.currentAssetAddress = vars.userBorrowedAssets[vars.assetIndex];
...
      //@audit currentAssetData.groupList.values() will also include yieldGroup (groupId 0)
|>    vars.assetGroupIds = currentAssetData.groupList.values();
...
      // same debt can be borrowed in different groups by different collaterals
      // e.g. BAYC borrow ETH in group 1, MAYC borrow ETH in group 2
      vars.userAssetDebtInBaseCurrency = 0;
      for (vars.groupIndex = 0; vars.groupIndex < vars.assetGroupIds.length; vars.groupIndex++) {
        vars.currentGroupId =
Withdraw/ Borrow native ETH might fail due to incorrect implementation of fund transfer flows
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/modules/IsolateLending.sol#L51


# Vulnerability details

## Impact
Withdraw/Borrow native ETH might fail due to incorrect implementation of fund transfer flows.

## Proof of Concept
When withdraw supplied ERC20 or borrow ERC20, assets are transferred to a specified  `receiver`, instead of msg.sender. However, when withdraw or borrow native ETH, current implementations assume WETH is transferred to msg.sender. This is incorrect. There are three instances:

(1) IsolateLending::isolateBorrow;
When a user isolate borrows, the debt assets(ERC20) will [be transferred to receiver address](https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/IsolateLogic.sol#L88), not msg.sender. This is enforced in IsolateLogic::executeIsolateBorrow().
```solidity
//src/libraries/logic/IsolateLogic.sol
  function executeIsolateBorrow(InputTypes.ExecuteIsolateBorrowParams memory params) internal returns (uint256) {
...
    VaultLogic.erc20TransferOutLiquidity(debtAssetData, params.receiver, vars.totalBorrowAmount);
...
```
(https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/IsolateLogic.sol#L88)

There is a special case where a user borrows native ETH. In this case, the debt asset is still WETH, and IsolateLending::isolateBorrow will [unwrap WETH into ETH](https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/VaultLogic.sol#L793). The intended fund transfer flow: PoolManager transfers WETH to the receiver, and the receiver’s WETH is pulled by poolManager and converted to ETH, ETH is transferred back to the receiver.

The problem is the implementation of WETH unwrapping in IsolateLending::isolateBorrow is incorrect. When isNative is true, VaultLogic.unwrapNativeTokenInWallet() will attempt to pull WETH from msgSender instead of the receiver. Because msgSender doesn't receive WETH, VaultLogic.unwrapNativeTokenInWallet() will revert, causing isolateBorrow to fail.
```solidity
//src/modules/IsolateLending.sol
  function isolateBorrow(
    uint32 poolId,
    address nftAsset,
    uint256[] calldata nftTokenIds,
    address asset,
    uint256[] calldata amounts,
    address onBehalf,
    address receiver
  ) public whenNotPaused nonReentrant {
    address msgSender = unpackTrailingParamMsgSender();
    DataTypes.PoolStorage storage ps = StorageSlot.getPoolStorage();
    bool isNative;
    if (asset == Constants.NATIVE_TOKEN_ADDRESS) {
      isNative = true;
      asset = ps.wrappedNativeToken;
    }

    uint256 totalBorrowAmount = IsolateLogic.executeIsolateBorrow(
      InputTypes.ExecuteIsolateBorrowParams({
        msgSender: msgSender,
        poolId: poolId,
        nftAsset: nftAsset,
        nftTokenIds: nftTokenIds,
        asset: asset,
        amounts: amounts,
        onBehalf:
Incorrect index used to calculate totalSupply, which invalidates asset / staker level yield cap checks
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/YieldLogic.sol#L51


# Vulnerability details

## Impact
Incorrect index used to calculate `totalSupply`, which invalidates asset / staker level yield cap checks.

## Proof of Concept
YieldLogic::executeYieldBorrowERC20 used borrowIndex to calculate `totalSupply`, when it should use assetData’s supplyIndex. 

An incorrect `totalSupply` is used to check the asset-level yield cap and staker-level yield cap, which invalidates yield cap checks. 
```solidity
//src/libraries/logic/YieldLogic.sol
  function executeYieldBorrowERC20(InputTypes.ExecuteYieldBorrowERC20Params memory params) internal {
...
     //@audit incorrect index is used to calculate totalSupply, it should use assetData.supplyIndex
|>   vars.totalSupply = VaultLogic.erc20GetTotalCrossSupply(assetData, groupData.borrowIndex);

    // check asset level yield cap limit
    vars.totalBorrow = VaultLogic.erc20GetTotalCrossBorrowInGroup(groupData, groupData.borrowIndex);
    require(
      (vars.totalBorrow + params.amount) <= vars.totalSupply.percentMul(assetData.yieldCap),
      Errors.YIELD_EXCEED_ASSET_CAP_LIMIT
    );

    // check staker level yield cap limit
    vars.stakerBorrow = VaultLogic.erc20GetUserCrossBorrowInGroup(groupData, vars.stakerAddr, groupData.borrowIndex);
    require(
      (vars.stakerBorrow + params.amount) <= vars.totalSupply.percentMul(ymData.yieldCap),
      Errors.YIELD_EXCEED_STAKER_CAP_LIMIT
    );
...
```
(https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/YieldLogic.sol#L51)

When `ymData.yieldCap` is exceeded, users can overborrow and the protocol takes on unprotected risks.

Flows: YieldStakingBase::stake -> poolYield.yieldBorrowERC20() -> YieldLogic::executeYieldBorrowERC20()

## Tools Used
Manual

## Recommended Mitigation Steps
Change into `assetData.supplyIndex` for `vars.totalSupply` calculation.


## Assessed type

Error
Incorrect accounting of utilization, supply / borrow rates due to vulnerable implementation in IsolateLogic::executeIsolateLiquidate
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/IsolateLogic.sol#L461


# Vulnerability details

## Impact
Incorrect accounting of utilization, supply / borrow rates due to vulnerable implementation in `IsolateLogic::executeIsolateLiquidate`.

## Proof of Concept
When an isolate loan ( borrow against a single nft token) is auctioned and liquidated, the liquidator (caller of IsolateLogic::executeIsolateLiquidate) may need to repay extra amount of debt(`vars.totalExtraAmount`) if the bid amount doesn't fully cover total debt(`vars.totalBorrowAmount`).

The problem is the above case is not correctly implemented when interests rates are updated (`InterestLogic.updateInterestRates(poolData, debtAssetData, (vars.totalBorrowAmount + vars.totalExtraAmount), 0)`).

In InterestLogic::updateInterestRates, utilization is recalculated based on `liquidityAdded` and `liquidityTaken`. In the context of `IsolateLogic::executeIsolateLiquidate`, the `liquidtyAdded` for a loan is the repayment of debt (vars.borrowAmount) which includes `loanData.bidAmount` + extraAmount.
```solidity
//src/libraries/logic/IsolateLogic.sol
  function executeIsolateLiquidate(InputTypes.ExecuteIsolateLiquidateParams memory params) internal {
...
    for (vars.nidx = 0; vars.nidx < params.nftTokenIds.length; vars.nidx++) {
...
      // Last bid can not cover borrow amount and liquidator need pay the extra amount
      if (loanData.bidAmount < vars.borrowAmount) {
|>      vars.extraBorrowAmounts[vars.nidx] = vars.borrowAmount - loanData.bidAmount;//@audit-info note: vars.borrowAmount = extraAmount + loanData.bidAmount
      }
...
    // update interest rate according latest borrow amount (utilizaton)
      //@audit (vars.totalBorrowAmount + vars.totalExtraAmount) is incorrect input, which inflates `liquidityAdded` in updateInterestRates.
|>    InterestLogic.updateInterestRates(poolData, debtAssetData, (vars.totalBorrowAmount + vars.totalExtraAmount), 0);

    // bid already in pool and now repay the borrow but need to increase liquidity
      //@audit-info note: only total debt (vars.totalBorrowAmount) are added to liquidity.
|>    VaultLogic.erc20TransferOutBidAmountToLiqudity(debtAssetData, vars.totalBorrowAmount);
...
```
(https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/IsolateLogic.sol#L461)

In InterestLogic::updateInterestRates, `(vars.totalBorrowAmount + vars.totalExtraAmount)` inflates `liquidityAdded`, which inflates `vars.availableLiquidityPlusDebt` and deflates utilization rate `vars.assetUtilizationRate`. This will cause an inflated borrow rate / supply rate based on the interest rate model. 
```solidity

  function updateInterestRates(
    DataTypes.PoolData storage poolData,
    DataTypes.AssetData storage assetData,
    uint256 liquidityAdded,
    uint256 liquidityTaken
  ) internal {
...
    // calculate the total asset supply
supplyIndex will be arbitrarily inflated due to vulnerable implementation in calculateLinearInterest and _updateSupplyIndex
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/InterestLogic.sol#L267
https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/math/MathUtils.sol#L33


# Vulnerability details

## Impact
`supplyIndex` will be arbitrarily inflated due to vulnerable implementation in `calculateLinearInterest()` and `_updateSupplyIndex()`.

## Proof of Concept
Unlike borrowIndex, supplyIndex uses a linear increase formula (rate * deltaT).

The issue is implementation in `calculateLinearInterest()` and `_updateSupplyIndex()` is incorrect, and will arbitrarily compound `supplyIndex` depending on how often `_updateSupplyIndex()` is invoked in borrow, repay, liquidation flows.

`calculateLinearInterest()` adds WadRayMath.RAY to the calculated result, effectively turning it into a multiplier that compounds the interest whenever rayMul is used in `_updateSupplyIndex()`.
```solidity
//src/libraries/math/MathUtils.sol
  function calculateLinearInterest(
    uint256 rate,
    uint256 lastUpdateTimestamp,
    uint256 currentTimestamp
  ) internal pure returns (uint256) {
    //solium-disable-next-line
    uint256 result = rate * (currentTimestamp - lastUpdateTimestamp);
    unchecked {
      result = result / SECONDS_PER_YEAR;
    }

|>    return WadRayMath.RAY + result;
  }
```
(https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/math/MathUtils.sol#L33)

Each time `_updateSupplyIndex()` is invoked, the interest is compounded rather than linearly increased.
```solidity
//src/libraries/logic/InterestLogic.sol
  function _updateSupplyIndex(DataTypes.AssetData storage assetData) internal {
    // Only cumulating on the supply side if there is any income being produced
    // The case of Fee Factor 100% is not a problem (supplyRate == 0),
    // as liquidity index should not be updated
    if (assetData.supplyRate != 0) {
      uint256 cumulatedSupplyInterest = MathUtils.calculateLinearInterest(
        assetData.supplyRate,
        assetData.lastUpdateTimestamp
      );
|>      uint256 nextSupplyIndex = cumulatedSupplyInterest.rayMul(assetData.supplyIndex);
      assetData.supplyIndex = nextSupplyIndex.toUint128();
    }
  }
```
(https://github.com/code-423n4/2024-07-benddao/blob/117ef61967d4b318fc65170061c9577e674fffa1/src/libraries/logic/InterestLogic.sol#L267)

POC:
suppose during the same time delta, a total of 6% linear distribution ( `calculateLinearInterest()` returns (1 + 6%) ). supplyIndex starts from 1 (leaving out precision scaling for simplicity):

Case A: _updateSupplyIndex() is invoked three times during this time delta with each step linear increase 2% ( at each time `calculateLinearInterest()` returns (1 + 2%) ).

supplyIndex after = 1 x (1 + 0.02)^3 -> 1.061208

Case B: _updateSupplyIndex() is invoked once at the end of this time delta with total linear increase of 6%.

supplyInde
It's impossible to retrieve collected fines from the yield staking contract
Audit: 2024-07-benddao-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-benddao/blob/main/src/yield/YieldStakingBase.sol#L329-L337
https://github.com/code-423n4/2024-07-benddao/blob/main/src/yield/YieldStakingBase.sol#L407
https://github.com/code-423n4/2024-07-benddao/blob/main/src/yield/YieldStakingBase.sol#L426


# Vulnerability details

## Impact
No means to retrieve collected fines from the staking contract.

## Proof of Concept
If `botAdmin` forcefully unstakes a position, a staker will have to pay an additional fine when the stake is repayed:
```solidity
  function _repay(uint32 poolId, address nft, uint256 tokenId) internal virtual {
    ---SNIP---

    vars.nftDebt = _getNftDebtInUnderlyingAsset(sd);
>>  vars.nftDebtWithFine = vars.nftDebt + sd.unstakeFine;

    // compute repay value
    if (vars.claimedYield >= vars.nftDebtWithFine) {
      vars.remainAmount = vars.claimedYield - vars.nftDebtWithFine;
    } else {
      vars.extraAmount = vars.nftDebtWithFine - vars.claimedYield;
    }

    // transfer eth from sender
    if (vars.extraAmount > 0) {
>>    underlyingAsset.safeTransferFrom(vars.nftOwner, address(this), vars.extraAmount);
    }

    if (vars.remainAmount > 0) {
      underlyingAsset.safeTransfer(vars.nftOwner, vars.remainAmount);
    }

    // repay lending pool
>>  poolYield.yieldRepayERC20(poolId, address(underlyingAsset), vars.nftDebt);

    poolYield.yieldSetERC721TokenData(poolId, nft, tokenId, false, address(underlyingAsset));

    // update shares
    accountYieldInWithdraws[address(vars.yieldAccout)] -= sd.withdrawAmount;
    totalDebtShare -= sd.debtShare;

    delete stakeDatas[nft][tokenId];

    emit Repay(msg.sender, nft, tokenId, vars.nftDebt);
  }
```
Note that the user's total debt consists of the actual NFT debt and the fine. When the stake is paid off, only the `nftDebt` is sent to the pool, and the `unstakeFine` remains in the contract. However, there are no functions that send the collected funds to the admin, and so they will remain in the contract forever.

## Tools Used
Manual review

## Recommended Mitigation Steps
Implement a function that allows admin to collect fines from the yield staking contract.


## Assessed type

Token-Transfer