xanoutas/solidity-security-findings huggingface dataset:
DSS cannot force jailed operator to unallocate from such DSS
Audit: 2024-07-karak-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/53eb78ebda718d752023db4faff4ab1567327db4/src/Core.sol#L130-L141
https://github.com/code-423n4/2024-07-karak/blob/53eb78ebda718d752023db4faff4ab1567327db4/src/Core.sol#L113-L124


# Vulnerability details

## Impact
According to https://github.com/code-423n4/2024-07-karak?tab=readme-ov-file#operators, `Operators can be jailed by the DSS` and one of the effects of jailing is `forcing them to unallocate from the DSS`. However, the following `Core.requestUpdateVaultStakeInDSS` and `Core.unregisterOperatorFromDSS` functions all require `msg.sender` to be the operator so only the operator can choose to unallocate from the DSS; although the operator is jailed by the DSS, the DSS is unable to call the `Core.requestUpdateVaultStakeInDSS` function for requesting the unstaking of such operator's vault from the DSS or the `Core.unregisterOperatorFromDSS` function for unregistering such operator from the DSS. Thus, the DSS fails to force the jailed operator to unallocate from the DSS and cannot prevent all further interactions caused by such operator.

https://github.com/code-423n4/2024-07-karak/blob/53eb78ebda718d752023db4faff4ab1567327db4/src/Core.sol#L130-L141
```solidity
    function requestUpdateVaultStakeInDSS(Operator.StakeUpdateRequest memory vaultStakeUpdateRequest)
        external
        nonReentrant
        whenFunctionNotPaused(Constants.PAUSE_CORE_REQUEST_STAKE_UPDATE)
        returns (Operator.QueuedStakeUpdate memory updatedStake)
    {
        address operator = msg.sender;
        CoreLib.Storage storage self = _self();
        self.checkIfOperatorIsRegInRegDSS(operator, vaultStakeUpdateRequest.dss);
        updatedStake = self.requestUpdateVaultStakeInDSS(vaultStakeUpdateRequest, self.nonce++, operator);
        emit RequestedStakeUpdate(updatedStake);
    }
```

https://github.com/code-423n4/2024-07-karak/blob/53eb78ebda718d752023db4faff4ab1567327db4/src/Core.sol#L113-L124
```solidity
    function unregisterOperatorFromDSS(IDSS dss, bytes memory unregistrationHookData)
        external
        nonReentrant
        whenFunctionNotPaused(Constants.PAUSE_CORE_UNREGISTER_FROM_DSS)
    {
        address operator = msg.sender;
        CoreLib.Storage storage self = _self();
        self.checkIfOperatorIsRegInRegDSS(operator, dss);
        self.unregisterOperatorFromDSS(dss, operator, unregistrationHookData);

        emit UnregisteredOperatorToDSS(operator, address(dss));
    }
```

## Proof of Concept
The following steps can occur for the described scenario.
1. Operator A is registered with DSS A and stakes one of its vaults to DSS A.
2. DSS A detects that Operator A's staked vault is too overleveraged so it jails Operator A.
3. DSS A wants to force Operator A to unallocate from DSS A.
4. Because the `Core.requestUpdateVaultStakeInDSS` function cannot be called by DSS A for unstaking Operator A's vault from DSS A and the `Core.unregisterOperatorFromDSS` function cannot be called
Delayed Slashing Window and Lack of Transparency for Pending Slashes Could Lead to Loss of Funds
Audit: 2024-07-karak-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L248
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/SlashingHandler.sol#L52
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/SlasherLib.sol#L79


# Vulnerability details

## **Vulnerability Details:**

Stakers can provide liquidity by choosing an operator and vault to deposit their assets with. In return, they receive shares based on their deposited amount relative to the total assets in the vault. 

DSSs have the right to slash vaults if it feels that an operator has failed to perform its tasks adequately. DSSs are subjected to a delay of 2 days (represented by VETO_WINDOW) before a slashing can be finalized. This allows the slashing committee to veto a slashing event if it feels that the slashing was unfair.

The [handleSlashing](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/SlashingHandler.sol#L52) function transfers the slashed assets from the vault, effectively reducing the total assets in the vault, which slashes each shareholder relative to the number of shares they own.

```solidity
    function handleSlashing(IERC20 token, uint256 amount) external {
        if (amount == 0) revert ZeroAmount();
        if (!_config().supportedAssets[token]) revert UnsupportedAsset();

        SafeTransferLib.safeTransferFrom(address(token), msg.sender, address(this), amount);
        // Below is where custom logic for each asset lives
        SafeTransferLib.safeTransfer(address(token), address(0), amount);
    }
```

The problem with the current system is that the two-day veto window introduces a time gap between when a slash is confirmed and when it is finalized. The slash request records the amount that should be slashed depending on the slash percentage and total assets in the [fetchEarmarkedStakes](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/SlasherLib.sol#L79) function. Therefore, it is fair to assume that the shareholders at that timestamp should be slashed. However, currently, users who deposit between the slash request and when it is executed will also be slashed and lose value.

Furthermore, the protocol currently lacks any getter methods to warn users of pending slashes for vaults, meaning users could unknowingly deposit into such vaults and lose value in a short period.

In the worst-case scenario, if a vault is fully slashed and the total assets become zero, since the total supply will still be non-zero and previous users will still own shares, any new deposits will instantly lose value, and some funds will be incorrectly allocated to previous users.

## **Impact:**

The current implementation can lead to unfair slashing of users who deposit between the slashing request and its finalization. Users unknowingly depositing
The SlashingHandler will always fail to handleSlashing for fee-on-transfer and balance-changes-outside-of-transfers tokens
Audit: 2024-07-karak-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/SlashingHandler.sol#L58


# Vulnerability details

## Impact
The `SlashingHandler` will be unusable for fee-on-transfer tokens, and the `handleSlashing` function will always fail to slash this type of tokens due to a revert.

In fee-on-transfer tokens, the actual transferred amount is always less than the `amount` that was requested to be transferred, as the token contract charges its own fee.

## Proof of Concept
```solidity
    /// @notice slashes the specified amount of the token
    /// The vault needs to give approval to the slashing handler prior to slashing
    /// Pulls the assets form the vault and send them to address(0)
    /// @param token address of the token to be slashed
    /// @param amount quantity of assets to be slashed
    function handleSlashing(IERC20 token, uint256 amount) external {
        if (amount == 0) revert ZeroAmount();
        if (!_config().supportedAssets[token]) revert UnsupportedAsset();

        SafeTransferLib.safeTransferFrom(address(token), msg.sender, address(this), amount);
        // Below is where custom logic for each asset lives
        SafeTransferLib.safeTransfer(address(token), address(0), amount);
    }
```

You can see that the amount that the vault passes to be slashed (`amount`) is passed to the `safeTransferFrom` call.

If the `token` is a fee-on-transfer token, the **real amount that the `SlashingHandler` will receive after the `transferFrom` call completes** will be less than the initial `amount`.

So the `SlashingHandler` at that point has the token value of `amount - fee`.

Due to that, at the `SafeTransferLib.safeTransfer(address(token), address(0), amount);` step, the `token` contract will revert due to `SlashingHandler`'s balance being less than the `amount` passed.

## Tools Used
Manual review.

## Recommended Mitigation Steps
Passing the initial `amount` directly is incorrect, and, *to properly account for fee-on-transfer tokens*, it should be replaced with the following logic:
```diff
    /// @notice slashes the specified amount of the token
    /// The vault needs to give approval to the slashing handler prior to slashing
    /// Pulls the assets form the vault and send them to address(0)
    /// @param token address of the token to be slashed
    /// @param amount quantity of assets to be slashed
    function handleSlashing(IERC20 token, uint256 amount) external {
        if (amount == 0) revert ZeroAmount();
        if (!_config().supportedAssets[token]) revert UnsupportedAsset();

        SafeTransferLib.safeTransferFrom(address(token), msg.sender, address(this), amount);
        // Below is where custom logic for each asset lives
        uint realAmount = token.balanceOf(address(this)); // can also slash dust amounts that can occur in the contract outside of the function calls
        SafeTransferLib.safeTransfer(address(token), address(0), realAmount);
    }
```


##
Share and asset calculations are not compatible with fee-on-transfer tokens
Audit: 2024-07-karak-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Vault.sol#L78-L119


# Vulnerability details

## Impact 
Share and asset calculations are incorrect, causing initial depositors to lose funds.
The shares per deposited unit of asset will be on an increasing scale so subsequent users get more shares for the same amount of assets; meaning they have a higher claim on the funds in the vault.

## Vulnerability Details
Given that rewards are not added to the vault; shares should always be minted on a 1:1 basis with the assets received.
However, fee-on-transfer tokens charge a fee to users for transfers of the token which breaks this invariant.

This issue affects the two `deposit()` functions and the `mint()` function in Vault.sol.

### Vault::deposit()
The wrong `assets` value is passed to `erc4626::deposit()` to calculate the shares a user will receive.
The actual assets received by the Vault will be less than the input parameter amount so the input param should be adjusted down before sending to `erc4626::deposit()`.

### Vault::mint()
The wrong `assets` amount is returned by `erc4626::mint()`; which calculates the assets a user must pay.
The actual assets received by the Vault will be less than the returned amount so the returned value should be adjusted up in `Vault::deposit()`.

## POC
Vault is set up to accept a Fee-On-Transfer token which has 2% fee

Bob, the first depositor, deposits 1000 tokens
  >> He receives 1000 shares
  >> The vault balance will be 980 tokens

Alice then deposits 1000 tokens 
  >> She receives 1020 Shares
  >> The vault balance will be 1960 tokens

Mike deposits 1000 tokens 
  >> He receives 1030 Shares
  >> The vault balance will be 2940 tokens

## Tools Used
Manual Review
Foundry Testing

## Recommendations
There should be a check in the `deposit()` fucntions whether the token being deposited is fee-on-transfer.
If the token is fee-on-transfer; the `assets` param should be adjusted down by the fee amount before sending to `erc4626::deposit()`.

There should be a check in the `mint()` function whether the token being deposited is fee-on-transfer.
If the token is fee-on-transfer; the `assets` param returned by `erc4626::mint()` should be adjusted up by the fee amount.


## Assessed type

ERC20
finalizeUpdateVaultStakeInDSS can be called by anyone on unregistered DSS 
Audit: 2024-07-karak-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L130
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L146


# Vulnerability details

## **Vulnerability Details:**

Operators select DSSs to allocate their funds to, and for each DSS, they can choose a list of vaults to stake. The [requestUpdateVaultStakeInDSS](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L130) function can be called by operators to stake a vault to a DSS they are registered to.

```solidity
    function requestUpdateVaultStakeInDSS(Operator.StakeUpdateRequest memory vaultStakeUpdateRequest)
        external
        nonReentrant
        whenFunctionNotPaused(Constants.PAUSE_CORE_REQUEST_STAKE_UPDATE)
        returns (Operator.QueuedStakeUpdate memory updatedStake)
    {
        address operator = msg.sender;
        CoreLib.Storage storage self = _self();
        self.checkIfOperatorIsRegInRegDSS(operator, vaultStakeUpdateRequest.dss);
        updatedStake = self.requestUpdateVaultStakeInDSS(vaultStakeUpdateRequest, self.nonce++, operator);
        emit RequestedStakeUpdate(updatedStake);
    }
```

This can then be finalized with the [finalizeUpdateVaultStakeInDSS](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L146) function after the MIN_STAKE_UPDATE_DELAY has passed, allowing the vault to be fully staked to the DSS.

```solidity
    function finalizeUpdateVaultStakeInDSS(Operator.QueuedStakeUpdate memory queuedStake)
        external
        nonReentrant
        whenFunctionNotPaused(Constants.PAUSE_CORE_FINALIZE_STAKE_UPDATE)
    {
        _self().validateAndUpdateVaultStakeInDSS(queuedStake);
        emit FinishedStakeUpdate(queuedStake);
    }
```

However, there is a scenario where a vault stake finalization can be executed to a DSS that is no longer registered to the operator. Since the check for the operator's registration is done only at the request stage, if an operator unregisters from the DSS, anyone can still call the finalizeUpdateVaultStakeInDSS function to finalize the stake of the operator's vault even though it is no longer registered.

This issue was identified and marked as “fixed” here:

- Issue M1: [https://github.com/code-423n4/2024-07-karak/blob/main/audits/core/Renascence - Karak Core Audit Report.pdf](https://github.com/code-423n4/2024-07-karak/blob/main/audits/core/Renascence%20-%20Karak%20Core%20Audit%20Report.pdf)

However, the issue was not actually fixed. Furthermore, in the previously identified issue, the finalizeUpdateVaultStakeInDSS function was limited to only the operator. In the current code, it is open to be called by anyone, significantly increasing the chances of the problem occurring.

## **Impact:**

The impact is the same as mentioned when the issue was identified in the previous audit. The operator's
Slashing’s will Always Fail In Some Cases
Audit: 2024-07-karak-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L220
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Vault.sol#L193
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/SlashingHandler.sol#L52


# Vulnerability details

## **Vulnerability Details:**

The [requestSlashing](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L220) function allows a slashing to be requested for a given operator’s deployed vaults staked to the DSS.  The slashing request must pass the SLASHING_VETO_WINDOW (2 days) before it can be confirmed, allowing the veto committee to cancel any unfair queued slashing.

This time gap can create situations where the requested slashed amount is no longer possible, as the contract might have had previous withdrawals or been slashed by other DSS’s in that time, reducing its overall balance. The [slashAssets](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Vault.sol#L193) function in the vault contract handles this by taking the minimum of the requested slashed amount and the contract balance.

```solidity
    function slashAssets(uint256 totalAssetsToSlash, address slashingHandler)
        external
        onlyCore
        returns (uint256 transferAmount)
    {
        transferAmount = Math.min(totalAssets(), totalAssetsToSlash);

        // Approve to the handler and then call the handler which will draw the funds
        SafeTransferLib.safeApproveWithRetry(asset(), slashingHandler, transferAmount);
        ISlashingHandler(slashingHandler).handleSlashing(IERC20(asset()), transferAmount);

        emit Slashed(transferAmount);
    }
```

However, if the total assets in the contract are zero, the transferAmount will be zero. When this zero value is passed to the [handleSlashing](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/SlashingHandler.sol#L52) function, it will revert due to a check that ensures the amount is not zero.

- Since DSS can slash 100% of a vault and vaults can be staked to multiple DSS, it is possible that a vault could be slashed before this slashing request, leaving its total assets as zero.
- Another scenario is if there were pending withdrawals that were completed before the slashing, resulting in the total assets being zero after the withdrawals.

```solidity

    function handleSlashing(IERC20 token, uint256 amount) external {
        if (amount == 0) revert ZeroAmount();
        if (!_config().supportedAssets[token]) revert UnsupportedAsset();

        SafeTransferLib.safeTransferFrom(address(token), msg.sender, address(this), amount);
        // Below is where custom logic for each asset lives
        SafeTransferLib.safeTransfer(address(token), address(0), amount);
    }
```

As a result, if this slashing reve
Collateral Trackers can be made for tokens without code.
Audit: 2024-09-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-09-panoptic/blob/881a306eeb3764a2553eeb74c69bf85f4b6ce438/contracts/PanopticFactory.sol#L171-L243


# Vulnerability details


Panoptic uses the safeTransferLib adapted from solady for the safe transfers of tokens to and fro the core components like 
- collateralTrackers
- Panoptic Pool
- Panoptic Factory

The safe transfer lib uses assembly for low level calls to the token addresses, this allows for an issue that can be exploited where calls to tokens without code will not revert.
meaning calling transfer or transferForm on such addresses will return success.
before we look at the attack another thing to consider is that during the deployment of new pools, the check that's supposed to prevent this can be bypassed given anyone can create a uniswap V3 pool
```solidity
 /// @notice Create a new Panoptic Pool linked to the given Uniswap pool identified uniquely by the incoming parameters.
    /// @dev Pool deployment is restricted to the factory owner until transferred to the zero address.
    /// @dev There is a 1:1 mapping between a Panoptic Pool and a Uniswap Pool.
    /// @dev A Uniswap pool is uniquely identified by its tokens and the fee.
    /// @param token0 Address of token0 for the underlying Uniswap v3 pool
    /// @param token1 Address of token1 for the underlying Uniswap v3 pool
    /// @param fee The fee tier of the underlying Uniswap v3 pool, denominated in hundredths of bips
    /// @param salt User-defined salt used in CREATE2 for the PanopticPool (must contain caller addr as first 20 bytes)
    /// @return newPoolContract The address of the newly deployed Panoptic pool
    function deployNewPool(
        address token0,
        address token1,
        uint24 fee,
        bytes32 salt
    ) external returns (PanopticPool newPoolContract) {
// more code
//@audit checks if pool has been created
        IUniswapV3Pool v3Pool = IUniswapV3Pool(UNIV3_FACTORY.getPool(token0, token1, fee));
        if (address(v3Pool) == address(0)) revert Errors.UniswapPoolNotInitialized();

        if (address(s_getPanopticPool[v3Pool]) != address(0))
            revert Errors.PoolAlreadyInitialized();
// more code
        newPoolContract = PanopticPool(POOL_REFERENCE.cloneDeterministic(salt));

        // Deploy collateral token proxies
        CollateralTracker collateralTracker0 = CollateralTracker(
            Clones.clone(COLLATERAL_REFERENCE)
        );
        CollateralTracker collateralTracker1 = CollateralTracker(
            Clones.clone(COLLATERAL_REFERENCE)//reorgs
        );

        // Run state initialization sequence for pool and collateral tokens
        collateralTracker0.startToken(true, token0, token1, fee, newPoolContract);
        collateralTracker1.startToken(false, token0, token1, fee, newPoolContract);
// some code

        // Mints the full-range initial deposit
        // which is why the deployer becomes also a "donor" of full-range liquidity
        // The SFPM will `safeTran
PanopticPool liquidation should use BP_DECREASE_BUFFER to detect solvency for liquidator
Audit: 2024-09-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-09-panoptic/blob/main/contracts/PanopticPool.sol#L1061


# Vulnerability details


## Impact

Liquidator solvency status may fall below `BP_DECREASE_BUFFER` due to decrease of buying power while performing liquidation.

## Bug Description

After performing liquidation, since liquidator's buying power may decrease, the solvency status should be checked against `BP_DECREASE_BUFFER` rather than `NO_BUFFER`.

This issue was introduced in the latest diff.

```solidity
    /// @notice Multiplier in basis points for the collateral requirement in the event of a buying power decrease, such as minting or force exercising another user.
@>  uint256 internal constant BP_DECREASE_BUFFER = 13_333;

    /// @notice Multiplier for the collateral requirement in the general case.
    uint256 internal constant NO_BUFFER = 10_000;

    function liquidate(
        TokenId[] calldata positionIdListLiquidator,
        address liquidatee,
        TokenId[] calldata positionIdList
    ) external {
        ...
        // ensure the liquidator is still solvent after the liquidation
@>      _validateSolvency(msg.sender, positionIdListLiquidator, NO_BUFFER);

        emit AccountLiquidated(msg.sender, liquidatee, bonusAmounts);
    }
```

## Proof of Concept

N/A

## Tools Used

Manual Review

## Recommended Mitigation Steps

Use `BP_DECREASE_BUFFER` instead of `NO_BUFFER`.


## Assessed type

Invalid Validation