xanoutas/solidity-security-findings huggingface dataset:
sakshamguruji - No UniswapV2 Slippage Protection Enabled
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

sakshamguruji

Medium

# No UniswapV2 Slippage Protection Enabled

## Summary

At all places when interacting with UniV2 pool the minAmountOut has been hardcoded to 0 essentially meaning 100% slippage tolerance.

## Vulnerability Detail

1.) When LP is being liquidated after expiry the following function is invoked in redeemExpired() ->

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L349

All of the LP is liquidated here by calling the `removeLiquidity` on the UniV2 AMM pool ->

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L282

2.) We can see that all of the LP tokens are passed here but the minimum amounts of Ra and CT have been hardcoded to 0 ->

`(raReceived, ctReceived) =
            ammRouter.removeLiquidity(raAddress, ctAddress, lp, 0, 0, address(this), block.timestamp);`

Meaning any amount returned by the AMM is being accepted and if there's high slippage (very common in UniV2 pools with low liquidity) then the RA/CT returned would be way lesser than expected which is a loss for the user since less tokens will be reserved for redemptions.

More areas with with such 0 slippage protection ->

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L345

## Impact

No slippage protection leads to loss of Ra/Ct tokens received

## Code Snippet

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L282

## Tool used

Manual Review

## Recommendation

Add a tolerance min out to the liquidity removal.

Duplicate of #66
hunter_w3b - Double-Counting of Redemption Asset (RA) in `_redeemCtDsAndSellExcessCt` Function
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

hunter_w3b

Medium

# Double-Counting of Redemption Asset (RA) in `_redeemCtDsAndSellExcessCt` Function


## Summary

The `_redeemCtDsAndSellExcessCt` function in the Vault contract incorrectly handles the redemption of Cover Tokens (CT) and Depeg Swap (DS) tokens, leading to a double-counting of Redemption Asset (RA) tokens during the `redeemEarly` process. This results in users receiving excess amount of RA tokens.

## Vulnerability Detail

When a user calls `redeemEarlyLv` to redeem Liquidity Vault (LV) tokens, the `_liquidateLpPartial` function removes liquidity from the AMM and DS from flashSwapRouter contract. The `_redeemCtDsAndSellExcessCt` function is then called to redeem CT and DS tokens and sell any excess CT.

1. **Liquidity Removal via __liquidateUnchecked**
   - When you call the function _liquidateLpPartial, it triggers the liquidity removal mechanism via the __liquidateUnchecked function. 
   - This function removes liquidity from the liquidity pool and returns two values:
          - ra: The amount of ra tokens (the redeemable asset).
          - ammCtBalance: The balance of ct tokens (collateral tokens) that are still left over after removing liquidity.
        
```solidity
  function _liquidateLpPartial(
        State storage self,
        uint256 dsId,
        IDsFlashSwapCore flashSwapRouter,
        IUniswapV2Router02 ammRouter,
        uint256 lvRedeemed
    ) internal returns (uint256 ra) {
        uint256 raPerLp;
        uint256 ctPerLp;
        uint256 raPerLv;
        uint256 ammCtBalance;

        (raPerLv,, raPerLp, ctPerLp) = __calculateCtBalanceWithRate(self, flashSwapRouter, dsId);

          // ra = 50e18
          // ammCtBalance = 50e18
@>>        (ra, ammCtBalance) = __liquidateUnchecked(
            self,
            self.info.pair1,
            self.ds[dsId].ct,
            ammRouter,
            IUniswapV2Pair(self.ds[dsId].ammPair),
            MathHelper.convertToLp(raPerLv, raPerLp, lvRedeemed)
        );
```

2. **Incorrect Addition of ra**
   - After removing liquidity, the next step adds up the ra value by calling `_redeemCtDsAndSellExcessCt`.

At this point, ra = 50e18 from the liquidity removal. However, when `_redeemCtDsAndSellExcessCt` is called, it further adds ra from the redemption of covert token (ct and ds tokens) and sells excess ct to ra.


```solidity
     
        // ra += ra + redeemCtds ---> after this call    RA = 50e18 + 100e18   @audit Instead of adding Ra twice, we added it three times.
        ra += _redeemCtDsAndSellExcessCt(self, dsId, ammRouter, flashSwapRouter, ammCtBalance);
    }
```    
#### 3. **Calculation in `_redeemCtDsAndSellExcessCt`**

In `_redeemCtDsAndSellExcessCt`, the following steps occur:

1. **Retrieve `reservedDs`:**
   The amount of `ds` tokens available in the flashSwapRouter contract is fetched:

   ```solidity
   uint256 reservedDs = flashSwapRouter.getLvReserve(self.info.toId(), dsId);
vinica_boy - On-chain calculated tolerance does not provide slippage protection when depositing into LV
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

vinica_boy

High

# On-chain calculated tolerance does not provide slippage protection when depositing into LV

## Summary

When users deposit into the Liquidity Vault (LV), a portion of their RA is converted to CT + DS tokens, and CT + RA is provided as liquidity to the AMM pair. The protocol sets a tolerance level to ensure that at least a certain amount of RA and CT is successfully provided. However, this tolerance is calculated on-chain using the current state of the AMM reserves, which are subject to change. An attacker could frontrun the transaction, manipulating the reserves and leave the user deposit RA in a state where RA price ratio to CT is very low.
## Vulnerability Detail 
The function `__addLiquidityToAmmUnchecked()` is responsible for adding liquidity to the AMM by calculating the tolerance for the amount of RA and CT tokens to be provided. The tolerance is determined on-chain using the current state of the AMM's reserves:
```solidity
    function __addLiquidityToAmmUnchecked(
        State storage self,
        uint256 raAmount,
        uint256 ctAmount,
        address raAddress,
        address ctAddress,
        IUniswapV2Router02 ammRouter
    ) internal {
        (uint256 raTolerance, uint256 ctTolerance) =
            MathHelper.calculateWithTolerance(raAmount, ctAmount, MathHelper.UNIV2_STATIC_TOLERANCE);

        ERC20(raAddress).approve(address(ammRouter), raAmount);
        ERC20(ctAddress).approve(address(ammRouter), ctAmount);

        (address token0, address token1, uint256 token0Amount, uint256 token1Amount) =
            MinimalUniswapV2Library.sortTokensUnsafeWithAmount(raAddress, ctAddress, raAmount, ctAmount);
        (,, uint256 token0Tolerance, uint256 token1Tolerance) =
            MinimalUniswapV2Library.sortTokensUnsafeWithAmount(raAddress, ctAddress, raTolerance, ctTolerance);

        (,, uint256 lp) = ammRouter.addLiquidity(
            token0, token1, token0Amount, token1Amount, token0Tolerance, token1Tolerance, address(this), block.timestamp
        );

        self.vault.config.lpBalance += lp;
    }
```

The `raAmount` and `ctAmount` to be provided are calculated based on the current reserves of the pair through the `__provideLiquidityWithRatio()` function:
```solidity
 function __provideLiquidityWithRatio(
        State storage self,
        uint256 amount,
        IDsFlashSwapCore flashSwapRouter,
        address ctAddress,
        IUniswapV2Router02 ammRouter
    ) internal returns (uint256 ra, uint256 ct) {
        uint256 dsId = self.globalAssetIdx;

        uint256 ctRatio = __getAmmCtPriceRatio(self, flashSwapRouter, dsId);

        (ra, ct) = MathHelper.calculateProvideLiquidityAmountBasedOnCtPrice(amount, ctRatio);

        __provideLiquidity(self, ra, ct, flashSwapRouter, ctAddress, ammRouter, dsId);
    }
```

```solidity
function __provideLiquidity(
        State storage self,
        uint256 raAmount,
        uint256 ctAmount,
        IDsFlashSwapCore flashSwapRouter,
        address ctA
Pheonix - Exchange Rate not accounted for over various instances in the protocol causing discrepancy
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

Pheonix

High

# Exchange Rate not accounted for over various instances in the protocol causing discrepancy

## Summary
There are multiple instances where exchange rate of ra : ct + ds is not accounted for in conversion. This will cause loss of funds for protocol. 

## Vulnerability Detail
Let's take a function ``unsafeIssueToLv``
```Solidity
    function unsafeIssueToLv(State storage self, uint256 amount) internal {
        uint256 dsId = self.globalAssetIdx;


        DepegSwap storage ds = self.ds[dsId];


        self.psm.balances.ra.incLocked(amount);


        ds.issue(address(this), amount);
    }
```
When this function is called in ``__provideLiquidity()`` function which is invoked whenever we are providing liquidity to the amm pair.
The idea behind this is that we break incoming ra in such a way such that some ra is used to mint CT + DS and other ra is used as liquidity. This is done to ensure that the ratio of liquidity going to the pair is optimal. 
Suppose we need 2 ra to get 1 ct + 1 ds and user deposit 10 ds then 5 ra will be used to mint 2.5ct + 2.5ds and the remaining 5 will be used as liquidity.
But when we are calling ``unsafeIssueToLv()`` we are not accounting for this exchange rate and directly increasing 5 as amount. 
There are many instances where this is happened.  

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L390

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L122

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L368

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L331


## Impact
Lack of exchange rate accounting will cause direct loss of funds to the protocol 

## Code Snippet
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L390

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L122

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L368

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L331


## Tool used

Manual Review

## Recommendation
Account for exchange rate in all the instances

Duplicate of #119
0xNirix - Incorrect value from emptyReservePartial may cause user redemption requests to be blocked in LV
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0xNirix

Medium

# Incorrect value from emptyReservePartial may cause user redemption requests to be blocked in LV

### Summary

The incorrect return value from emptyReservePartial will cause a critical failure in the redemption process for LV users as the system will attempt to sell non-existent CT tokens, potentially blocking all redemptions.

### Root Cause

In RouterState contract for FlashSwapRouter the emptyReservePartial function returns the remaining reserve instead of the amount emptied, which is then misused in subsequent calculations:
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L80

```solidity
function emptyReservePartial(ReserveState storage self, uint256 dsId, uint256 amount, address to)
    internal
    returns (uint256 reserve)
{
    self.ds[dsId].ds.transfer(to, amount);
    self.ds[dsId].reserve -= amount;
    reserve = self.ds[dsId].reserve; // Returns remaining reserve
}
```
This return value is then used incorrectly in _redeemCtDsAndSellExcessCt:
```solidity
function _redeemCtDsAndSellExcessCt(
    State storage self,
    uint256 dsId,
    IUniswapV2Router02 ammRouter,
    IDsFlashSwapCore flashSwapRouter,
    uint256 ammCtBalance
) internal returns (uint256 ra) {
    uint256 reservedDs = flashSwapRouter.getLvReserve(self.info.toId(), dsId);
    uint256 redeemAmount = reservedDs >= ammCtBalance ? ammCtBalance : reservedDs;
    reservedDs = flashSwapRouter.emptyReservePartial(self.info.toId(), dsId, redeemAmount);
    ra += redeemAmount;
    PsmLibrary.lvRedeemRaWithCtDs(self, redeemAmount, dsId);
    uint256 ctSellAmount = reservedDs >= ammCtBalance ? 0 : ammCtBalance - reservedDs;
    // ... rest of the function
}
```

### Internal pre-conditions

1. The initial reservedDs needs to be greater than or equal to ammCtBalance
2. The emptyReservePartial function needs to empty most or all of the reserve, leaving a remaining amount less than ammCtBalance

### External pre-conditions

_No response_

### Attack Path

1. User initiates a redemption request in the LV
2. The system calculates the redeemAmount as ammCtBalance (since reservedDs >= ammCtBalance)
3. emptyReservePartial is called, emptying the reserve and returning the remaining amount
4. The returned value (remaining reserve) is stored in reservedDs
5. The system calculates ctSellAmount as a non-zero value (since the new reservedDs < ammCtBalance)
6. The system attempts to sell this non-existent CT amount, causing the transaction to revert

### Impact

The LV users cannot execute their redemption requests. This could potentially block all redemptions under certain conditions, leading to locked funds and a loss of trust in the protocol. The inability to redeem may cause a loss of funds for users, as exchange rates and other market conditions may change unfavorably before the issue is resolved, potentially reducing the value of their assets when they are finally able to redeem.

### PoC

_No resp
0x73696d616f - Users redeeming with `Ds` in the `Psm` will not decrease the correct amount of locked `Ra`, leading to stolen funds on withdrawals
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0x73696d616f

High

# Users redeeming with `Ds` in the `Psm` will not decrease the correct amount of locked `Ra`, leading to stolen funds on withdrawals

### Summary

[Psm::redeemRaWithDs()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/Psm.sol#L142) calls [PsmLib::redeemWithDs()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L364), which [calls](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L379) [PsmLib::_afterRedeemWithDs()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L329) to send the unlocked `Ra` to the owner and [decrease](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/RedemptionAssetManagerLib.sol#L70) `self.psm.balances.ra.locked` by calling [self.psm.balances.ra.unlockTo(owner, received);](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L346), that is, the amount of `Ra` locked in the contract. 

However, in the process, the `Ra` amount decreased from `self.psm.balances.ra.locked` is just the `received`, which has been [subtracted](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L342) the fee. But, this fee is used to provide liquidity, which means it is no longer locked and available, leading to the `Psm` having a bigger `self.psm.balances.ra.locked` than it should.

As such, on [PsmLib::_separateLiquidity()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L73), more `Ra` than what really exists is assumed to be [available](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/RedemptionAssetManagerLib.sol#L42) for withdrawal, which will make some users steal `Ra` when withdrawing and the last users not being able to withdraw.

### Root Cause

In `PsmLib:346`, the `Ra` locked is [decreased](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/RedemptionAssetManagerLib.sol#L70) by the received amount [minus](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L342) the fee.

### Internal pre-conditions

None.

### External pre-conditions

None.

### Attack Path

1. User calls `Psm::redeemWithDs()`.

### Impact

Users withdrawing first steal funds and the last users will not be able to withdraw their funds at all.

### PoC

In `PsmLib::_afterRedeemWithDs()`, `self.psm.balances.ra.unlockTo(owner, received);` is called with the received amount minus the fee:
```solidity
function _afterRedeemWithDs(
    State storage self,
    DepegSwap storage ds,
    address owner,
    uint256 amount,
    uint256 feeP
steadyman - Incorrect repurchase logic
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

steadyman

High

# Incorrect repurchase logic

## Summary
The incorrect repurchase logic allows attackers to set ra.self.locked in PSM to 0. When ra.self.locked is 0, the user cannot retrieve Ra in PSM when calling the redeemWithCT function, and Ra in PSM is locked forever.
## Vulnerability Detail
When the attacker calls the repurchase function to use Ra to purchase Pa and Ds, the contract uses the lockUnchecked function to transfer Ra, and self.locked will not increase. Then when the attacker calls the redeemRaWithDs function to redeem Ra with Pa and Ds, the contract uses the unlockTo function to transfer Ra, and self.locked will decrease.

Detailed example (all fees will be ignored to make the example simple enough):

0. PSM:
Bob: ra.balance = 100 pa.balance = 100
attacker: ra.balance = 100

1. Bob deposits 100Ra.
PSM: ra.locked = 100 ra.balance = 100
Bob: ds.balance = 100 ct.balacne = 100 pa.balance = 100

2. Bob buys 50Ra with 50Pa and 50DS.
PSM: ra.locked = 50 ra.balance = 50 pa.balance = 50 ds.balance = 50
Bob: ra.balance = 50 pa.balance = 50 ds.balance = 50 ct.balacne = 100
attacker: ra.balance = 100

3. The attacker uses 50Ra to purchase 50Pa and 50Ds.
PSM: ra.locked = 50 ra.balance = 100
Bob: ra.balance = 50 pa.balance = 50 ds.balance = 50 ct.balacne = 100
attacker: ra.balance = 50 pa.balance = 50 ds.balance = 50

4. The attacker uses 50Pa and 5 0Ds Buy 50Ra.
PSM: ra.balance = 50 pa.balance = 50 ds.balance = 50 ra.locked = 0
Bob: ra.balance = 50 pa.balance = 50 ds.balance = 50 ct.balacne = 100
attacker: ra.balance = 100

At this time, user bob will lose 50ra.
## Code Snippet
```solidity
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L310
```
## Tool used

Manual Review

## Recommendation
```solidity
    function repurchase(
        State storage self,
        address buyer,
        uint256 amount,
        IDsFlashSwapCore flashSwapRouter,
        IUniswapV2Router02 ammRouter
    ) internal returns (uint256 dsId, uint256 received, uint256 feePrecentage, uint256 fee, uint256 exchangeRates) {
        DepegSwap storage ds;

        (dsId, received, feePrecentage, fee, exchangeRates, ds) = previewRepurchase(self, amount);

        // decrease PSM balance
        // we also include the fee here to separate the accumulated fee from the repurchase
        self.psm.balances.paBalance -= (received);
        self.psm.balances.dsBalance -= (received);

        // transfer user RA to the PSM/LV
-       self.psm.balances.ra.lockUnchecked(amount, buyer);
+       self.psm.balances.ra.lockFrom(amount, buyer);
        // transfer user attrubuted DS + PA
        // PA
        (, address pa) = self.info.underlyingAsset();
        IERC20(pa).safeTransfer(buyer, received);

        // DS
        IERC20(ds._address).transfer(buyer, received);

        // Provide liquidity
        VaultLibrary.provideLiquidityWithFee(self, fee, flashSwapRouter, ammRouter);
    }
```

Duplicate of #126
0x73696d616f - Users repurchasing will transfer funds to the vault without tracking the `Ra` deposited which will leave stuck `Ra` in the `Psm`
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0x73696d616f

High

# Users repurchasing will transfer funds to the vault without tracking the `Ra` deposited which will leave stuck `Ra` in the `Psm`

### Summary

[Psm::repurchase()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/Psm.sol#L34) calls [PsmLib::repurchase()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L293) which transfers [Pa](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L315) and [Ds](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L318) to the user and [receives](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L310) `Ra` in return. However, this `Ra` is not [added](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/RedemptionAssetManagerLib.sol#L65) to `self.psm.balances.ra.locked`, which means that users will not be able to redeem it via [Psm::redeemRaWithDs()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/Psm.sol#L123) or [Psm::redeemWithCt()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/Psm.sol#L183) as the amount locked would [underflow](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L188) due to not having been increased earlier, or in case of `Psm::redeemWithCt()`, the [checkpointed](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L73) amount would not [include](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/RedemptionAssetManagerLib.sol#L42) the received amount.

### Root Cause

In `PsmLib:310`, `self.psm.balances.ra.lockUnchecked(amount, buyer);` is called instead of `self.psm.balances.ra.lockfrom(amount, buyer);`.

### Internal pre-conditions

None.

### External pre-conditions

None.

### Attack Path

1. Users repurchase `Pa` and `Ds` by selling `Ra` via `Psm::repurchase()`.

### Impact

Loss of the `Ra` transferred in `Psm::repurchase()`.

### PoC

`PsmLib::repurchase()` calls `self.psm.balances.ra.lockUnchecked(amount, buyer);`
```solidity
function repurchase(
    State storage self,
    address buyer,
    uint256 amount,
    IDsFlashSwapCore flashSwapRouter,
    IUniswapV2Router02 ammRouter
) internal returns (uint256 dsId, uint256 received, uint256 feePrecentage, uint256 fee, uint256 exchangeRates) {
    ...
    self.psm.balances.ra.lockUnchecked(amount, buyer); //@audit does not track RAs
    ...
}
```

`RedemptionAssetManagerLib::lockUnchecked()` does not increase the locked amount of `Ra`.
```solidity
function lockUnchecked(PsmRedemptionAssetManager storage self, uint256 amount, address from) internal {
    IERC20(self._a