xanoutas/solidity-security-findings huggingface dataset:
KupiaSec - Incorrect `psmRa` in the `VaultLib._liquidatedLp()` function
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

KupiaSec

High

# Incorrect `psmRa` in the `VaultLib._liquidatedLp()` function

## Summary

In the `VaultLib._liquidatedLp()` function, `PsmLibrary.lvRedeemRaWithCtDs(self, redeemAmount, dsId)` is called to redeem `RA` using `(CT + DS) * redeemAmount`. In this case, the redeemed amount of `RA` is `redeemAmount * exchangeRate`. However, it accounts the redeemed amount of `RA` as `redeemAmount` instead of `redeemAmount * exchangeRate`.

## Vulnerability Detail

As noted at line 377, it redeems `RA` using `(CT + DS) * redeemAmount`. Based on the exchange rate between `RA` and `CT + DS`, the redeemed amount of `RA` equals `redeemAmount * ds.exchangeRate()`. However, at line 390, only `redeemAmount`—rather than `redeemAmount * ds.exchangeRate()`—is added to `psmRa`. Consequently, an incorrect amount of `RA` is reserved in the withdrawal pool (`raAmm + psmRa`, as seen at line 392), leading to a loss of `RA` for users, as they will redeem `RA` from the withdrawal pool after expiry.

```solidity
    function _liquidatedLp(
        State storage self,
        uint256 dsId,
        IUniswapV2Router02 ammRouter,
        IDsFlashSwapCore flashSwapRouter
    ) internal {
        DepegSwap storage ds = self.ds[dsId];

        // if there's no LP, then there's nothing to liquidate
        if (self.vault.config.lpBalance == 0) {
            return;
        }

        // the following things should happen here(taken directly from the whitepaper) :
        // 1. The AMM LP is redeemed to receive CT + RA
        // 2. Any excess DS in the LV is paired with CT to redeem RA
        // 3. The excess CT is used to claim RA + PA in the PSM
        // 4. End state: Only RA + redeemed PA remains

        self.vault.lpLiquidated.set(dsId);

        (uint256 raAmm, uint256 ctAmm) = __liquidateUnchecked(
            self, self.info.pair1, self.ds[dsId].ct, ammRouter, IUniswapV2Pair(ds.ammPair), self.vault.config.lpBalance
        );

        uint256 reservedDs = flashSwapRouter.emptyReserve(self.info.toId(), dsId);

        uint256 redeemAmount = reservedDs >= ctAmm ? ctAmm : reservedDs;
377     PsmLibrary.lvRedeemRaWithCtDs(self, redeemAmount, dsId);

        // if the reserved DS is more than the CT that's available from liquidating the AMM LP
        // then there's no CT we can use to effectively redeem RA + PA from the PSM
        uint256 ctAttributedToPa = reservedDs >= ctAmm ? 0 : ctAmm - reservedDs;

        uint256 psmPa;
        uint256 psmRa;

        if (ctAttributedToPa != 0) {
            (psmPa, psmRa) = PsmLibrary.lvRedeemRaPaWithCt(self, ctAttributedToPa, dsId);
        }

390     psmRa += redeemAmount;

392     self.vault.pool.reserve(self.vault.lv.totalIssued(), raAmm + psmRa, psmPa);
    }
```

## Impact

Loss of `RA` for users.

## Code Snippet

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

## Tool used

Manual Review

## Recommendation

Consider the exchange rate.

```diff
    fu
Pheonix - Using block.timestamp as deadline does not equate instantaneous execute.
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

Pheonix

Medium

# Using block.timestamp as deadline does not equate instantaneous execute.

## Summary
Submitting deadline as block.timestamp is a bad idea since malicious miner can hold it until the transaction is favourable to them. 

## Vulnerability Detail
Using block.timestamp as the expiry or deadline of an operation doesn’t enforce immediate execution. Instead, it indicates that the caller is comfortable with the transaction being included in whichever block it is mined in. However, this opens the door for potential manipulation by malicious miners, who could delay the transaction (for example, through the flashbots mempool used for bundling transactions) until conditions are most advantageous to them. This delay could lead to maximum slippage under the transaction’s slippage tolerance or trigger unfavorable events like liquidations. To mitigate this risk, expiry times should be determined off-chain and set explicitly by the caller, reducing the potential for unnecessary Miner Extractable Value (MEV).

There are 3 instances 
1. ``__addLiquidityToAmmUnchecked()`` 
2. ``__liquidateUnchecked``
3. ``    function _redeemCtDsAndSellExcessCt()``

## Impact
 MEV exploitation. Could be considered Medium as it allows MEV to be extracted by delaying tx inclusion.

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

## Tool used

Manual Review

## Recommendation
Ensure a deadline parameter is passed. 

Duplicate of #66
0x73696d616f - Users of the `FlashSwapRouter` will repay too much `Ct` to the `Amm` due to mixing the `amountOut` and `repayAmount` variables
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0x73696d616f

High

# Users of the `FlashSwapRouter` will repay too much `Ct` to the `Amm` due to mixing the `amountOut` and `repayAmount` variables

### Summary

[FlashSwapRouter::_swapRaforDs()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L98) calls [DsFlashSwap::getAmountOutBuyDS()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L109), which calls [UniswapV2Library::getAmountIn()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/DsFlashSwap.sol#L189) to get the `repaymentAmount` of `Ct` tokens. However, this variable is not used anymore, and the `FlashSwapRouter` later on the `Uniswap` callback [FlashSwapRouter::uniswapV2Call() ](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L338) calls internally [FlashSwapRouter::__afterFlashswapBuy()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L349) which [repays](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L377) the flash loan with `ct` tokens equal to the `Ds` attributed, instead of the required `repaymentAmount` calculated earlier.

This means users will take losses and send too many `Ct` to the `Amm`, which are stolen later by some other user.

### Root Cause

In `FlashSwapRouter.sol:377`, all the received `Ct` tokens are repaid to the `Amm`.

### Internal pre-conditions

None.

### External pre-conditions

None.

### Attack Path

1. User calls `FlashSwapRouter::swapRaforDs()`, over repaying the flash loan.

### Impact

Users loses funds for bots that claim these funds later in the Amm.

### PoC

`FlashSwapRouter`
```solidity
function _swapRaforDs(
    ReserveState storage self,
    AssetPair storage assetPair,
    Id reserveId,
    uint256 dsId,
    uint256 amount,
    uint256 amountOutMin
) internal returns (uint256 amountOut) {
    uint256 borrowedAmount;

    // calculate the amount of DS tokens attributed
    (amountOut, borrowedAmount,) = assetPair.getAmountOutBuyDS(amount); //@audit repayment amount is ignored
    ...
}

...

function __afterFlashswapBuy(
    ReserveState storage self,
    Id reserveId,
    uint256 dsId,
    address caller,
    uint256 dsAttributed
) internal {
    AssetPair storage assetPair = self.ds[dsId];
    assetPair.ra.approve(owner(), dsAttributed);

    IPSMcore psm = IPSMcore(owner());
    psm.depositPsm(reserveId, dsAttributed);

    // should be the same, we don't compare with the RA amount since we maybe dealing
    // with a non-rebasing token, in which case the amount deposited and the amount received will always be different
    // so we simply enforce that the
vinica_boy - Buying DS with RA does not account for the exhange rate between RA: CT+DS
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

vinica_boy

High

# Buying DS with RA does not account for the exhange rate between RA: CT+DS

## Summary

From README file:

```text
Buying Depeg Swaps:

1. Buyer sends Redemption Asset into the swap contract 
2. A contract withdraws more Redemption Asset from the AMM
3. The Redemption Asset is used to mint Cover Token and Depeg Swap
4. Depeg Swap is sent to the buyer
5. The Cover Token is sold for Redemption Asset to return the amount from step 2
```

From 3 - RA is used to mint CT+DS but the protocol does not take into consideration the exchange rate when minting CT+DS, leading to a locked funds.
## Vulnerability Detail

The function `__afterFlashswapBuy()` is used during a flash swap to mint CT + DS, send the DS tokens to the user, and return CT tokens to the AMM in exchange for the borrowed RA. However, a significant issue arises due to the unchecked return value from `psm.depositPsm()`.

In this function, `dsAttributed` is assumed to represent the amount of DS the user should receive based on the amount of RA they provided in the `swapRaforDs()` operation. The assumption here is that the exchange rate between RA and DS is 1:1, which is incorrect.
```solidity
function __afterFlashswapBuy(
        ReserveState storage self,
        Id reserveId,
        uint256 dsId,
        address caller,
        uint256 dsAttributed
    ) internal {
        AssetPair storage assetPair = self.ds[dsId];
        assetPair.ra.approve(owner(), dsAttributed);

        IPSMcore psm = IPSMcore(owner());
        psm.depositPsm(reserveId, dsAttributed);

        // should be the same, we don't compare with the RA amount since we maybe dealing
        // with a non-rebasing token, in which case the amount deposited and the amount received will always be different
        // so we simply enforce that the amount received is equal to the amount attributed to the user

        // send caller their DS
        assetPair.ds.transfer(caller, dsAttributed);
        // repay flash loan
        assetPair.ct.transfer(msg.sender, dsAttributed);
    }
```

The `Psm.depositPsm()` function internally calls `PsmLib.deposit()` to calculate the correct amount of DS + CT tokens to mint based on the current exchange rate between RA and DS. However, `__afterFlashswapBuy()` does not account for the actual exchange rate and assumes that providing `X` amount of RA will mint `X` amount of DS, leading to a mismatch.
```solidity
function deposit(State storage self, address depositor, uint256 amount)
        internal
        returns (uint256 dsId, uint256 received, uint256 _exchangeRate)
    {
        if (amount == 0) {
            revert ICommon.ZeroDeposit();
        }

        dsId = self.globalAssetIdx;

        DepegSwap storage ds = self.ds[dsId];

        Guard.safeBeforeExpired(ds);
        _exchangeRate = ds.exchangeRate();

        received = MathHelper.calculateDepositAmountWithExchangeRate(amount, _exchangeRate);

        self.psm.balances.ra.lockFrom(amount, depositor);

        ds.
ivanonchain - No slippage parameter can lead users to receive a smaller amount than expected.
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

ivanonchain

High

# No slippage parameter can lead users to receive a smaller amount than expected.

### Summary

Users can be frontrun and receive a smaller amount than expected when attempting to redeem early funds by calling the `ValidLib.sol::redeemEarly()` function. The `UniswapV2Router02.sol::swapExactTokensForTokens()` function is called internally without specifying the amountTokenMin parameter. This opens the trade to potential delays by miners or network congestion, as well as the risk of a sandwich attack, resulting in the user receiving fewer funds than anticipated.

### Root Cause

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L345
In  `VaultLib.sol:345` hardcoding amountTokenMin to zero creates a lack of slippage protection

### Impact

In the absence of contract slippage protection, the level of impact of the attack is proportional to the level of slippage that the attacker can generate through market manipulation.


### Mitigation

Consider allowing the user to add a custom amountTokenMin parameter as a slippage guard to reduce potential manipulation attacks by mempool snipers.

Duplicate of #66
alphacipher - Interactions with AMMs do not use deadlines for operations, exposing transactions to potential front-running risks
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

alphacipher

Medium

# Interactions with AMMs do not use deadlines for operations, exposing transactions to potential front-running risks

## Summary

The absence of a deadline parameter in AMM interactions allows a malicious actor, such as a miner, to delay the execution of the transaction, exploiting maximum slippage or profiting from other market changes. This is especially critical in decentralized finance (DeFi) protocols where timing-sensitive operations are involved. The lack of deadline checks can create an opportunity for front-running and other miner-exploited vulnerabilities, resulting in financial losses or disrupted operations.

## Vulnerability Detail

Front-running is a significant concern in the design of Automated Market Makers (AMMs). By omitting the deadline parameter in operations that interact with AMMs, miners can delay the transaction intentionally, creating conditions where slippage is maximized. This delay can lead to situations where the transaction incurs higher-than-expected slippage or fails to execute under favorable conditions.

```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;

    DepegSwap storage ds = self.ds[dsId];
    address[] memory path = new address[](2);
    path[0] = ds.ct;
    path[1] = self.info.pair1;

    ERC20(ds.ct).approve(address(ammRouter), ctSellAmount);

    if (ctSellAmount != 0) {
        // 100% tolerance, to ensure this not fail
 @>>       ra += ammRouter.swapExactTokensForTokens(ctSellAmount, 0, path, address(this), block.timestamp)[1];
    }
}
```
Specifically, the function `_redeemCtDsAndSellExcessCt` interacts with UniswapV2 without including a deadline, allowing miners to hold the transaction, especially when liquidations or slippage are involved. In such cases, the miner may prioritize other transactions that are more profitable or delay the transaction to worsen the slippage condition.

## Impact

A miner can strategically withhold a transaction from being mined until the slippage is at its maximum, leading to a significant financial loss for the user. Additionally, if the transaction is performed to free up capital to avoid a liquidation, a miner may instead allow the liquidation to occur, profiting from follow-on transactions. The absence of deadline parameters in AMM-related functions creates a critical vulnerability that can be exploited, leading to significant financial damage.

## Code
0xNirix - Exchange Rate is neglected in DS-RA Swaps causing Issues like Fund Loss
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0xNirix

Medium

# Exchange Rate is neglected in DS-RA Swaps causing Issues like Fund Loss

### Summary

Failure to account for DS+CT to RA exchange rate will cause an incorrect valuation of user tokens for swappers as the contract will incorrectly calculate swap amounts and flash loan repayments.

### Root Cause

User Initiates Swap:
```solidity
function swapDsforRa(Id reserveId, uint256 dsId, uint256 amount, uint256 amountOutMin) external returns (uint256 amountOut) {
    AssetPair storage assetPair = reserves[reserveId].ds[dsId];
    assetPair.ds.transferFrom(msg.sender, address(this), amount);
    amountOut = __swapDsforRa(assetPair, reserveId, dsId, amount, amountOutMin);
}
```
At this point, amount is the DS amount, but it's not adjusted for the exchange rate.

Core Swap Logic in __swapDsforRa:
```solidity
function __swapDsforRa(AssetPair storage assetPair, Id reserveId, uint256 dsId, uint256 amount, uint256 amountOutMin) internal returns (uint256 amountOut) {
    (amountOut,) = assetPair.getAmountOutSellDS(amount);
    if (amountOut < amountOutMin) {
        revert InsufficientOutputAmount();
    }
    __flashSwap(assetPair, assetPair.pair, 0, amount, dsId, reserveId, false, amountOut);
}
```
getAmountOutSellDS doesn't consider the exchange rate, potentially undervaluing amountOut.

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/DsFlashSwap.sol#L166-L170
```solidity
    function getAmountOutSellDS(AssetPair storage assetPair, uint256 amount)
        internal
        view
        returns (uint256 amountOut, uint256 repaymentAmount)
    {
        (uint112 raReserve, uint112 ctReserve) = getReservesSorted(assetPair);
        // we calculate the repayment amount based on the imbalanced ct reserve since we borrow CT from the AMM
        repaymentAmount = MinimalUniswapV2Library.getAmountIn(amount, raReserve, ctReserve - amount);


        // the amountOut is essentially what the user receive, we can calculate this by simply subtracting the repayment amount
        // from the amount, since we're getting back the same RA amount as DS user buy, this works. to get the effective price per DS,
        // you would devide this by the DS amount user bought.
        // note that we subtract 1 to enforce uni v2 rules
        amountOut = amount - repaymentAmount;


        // enforce uni v2 rules, pay 1 wei more
        amountOut -= 1;
        repaymentAmount += 1;


        assert(amountOut + repaymentAmount == amount);
```

Flash Swap Initiation:
The __flashSwap function initiates the flash loan, borrowing the calculated amountOut of RA.
Flash Swap Callback (uniswapV2Call):
```solidity
function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
    // ... (verification code)
    __afterFlashswapSell(self, amount, reserveId, dsId, caller, extraData);
}
```

After Flash Swap Sell (__afterFlashswapSell):
```solidity
function __afterFlashswapSell(ReserveState sto
Pheonix - 100% tolerance might cause problems
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

Pheonix

Medium

# 100% tolerance might cause problems

## Summary
The protocol uses 0 as slippage parameter to ensure that the transaction should not revert. However, it can be problematic in certain scenarios.
## Vulnerability Detail
Take a look at ``_redeemCtDsAndSellExcessCt()``
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L318-L347

Here, the protocol is trying to use ``swapExactTokensForTokens`` to swap by providing 0 as slippage parameter. 
But the problem by providing no slippage parameter is that it is vulnerable to sandwhich attacks and final ra value is forced to be less than what is expected. 

There is one more place where lack of slippage parameter can cause problems
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L270-L288

## Impact
Less output amount than what is intended

## Code Snippet
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L318-L347
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultLib.sol#L270-L288
## Tool used

Manual Review

## Recommendation
Ensure a minimum slippage parameter provided. 

Duplicate of #66
0x73696d616f - `VaultPoolLib::reserve()` will store the `Pa` not attributed to user withdrawals incorrectly and leave in untracked once it expires again
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0x73696d616f

High

# `VaultPoolLib::reserve()` will store the `Pa` not attributed to user withdrawals incorrectly and leave in untracked once it expires again

### Summary

[VaultPoolLib::reserve()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultPoolLib.sol#L12) stores the `Pa` attributed to withdrawals in [self.withdrawalPool.stagnatedPaBalance](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultPoolLib.sol#L31) instead of storing the amount `attributedToAmm`. Additionally, this amount of `Pa`, the one attributed to the `Amm` is never dealt with and leads to stuck `PA`.

The comment in the code mentions 
```solidity
    // FIXME : this is only temporary, for now
    // we trate PA the same as RA, thus we also separate PA
    // the difference is the PA here isn't being used as anything
    // and for now will just sit there until rationed again at next expiry.
```
But it is incorrect as it is never rationed again, just forgotten. The [VaultPoolLib::rationedToAmm()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultPoolLib.sol#L170) function only uses the `Ra` [balance](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/VaultPoolLib.sol#L171), not the `Pa`, which is effectively left untracked.

### Root Cause

In `VaultPoolLib:170`, the leftover non attributed `Pa` is not dealt with.

### Internal pre-conditions

None.

### External pre-conditions

None.

### Attack Path

1. `VaultPoolLib::reserve()` is called when liquidating the lp position of the `Vault` via `VaultLib::_liquidatedLP()`, triggered by users when redeeming expired liquidity vault shares or on the admin trigerring a new issuance.

### Impact

The `Pa` in the `Vault` is stuck.

### PoC

`VaultPoolLib::rationedToAmm()` does not deal with the `Pa`.
```solidity
function rationedToAmm(VaultPool storage self, uint256 ratio) internal view returns (uint256 ra, uint256 ct) {
    uint256 amount = self.ammLiquidityPool.balance;

    (ra, ct) = MathHelper.calculateProvideLiquidityAmountBasedOnCtPrice(amount, ratio);
}
```

### Mitigation

Distributed the `Pa` to users based on their `LV` shares or redeem the `Pa` for `Ra` and add liquidity to the new issued `Ds` or similar.