xanoutas/solidity-security-findings huggingface dataset:
korok - Incorrect Price Ratio Calculation in calculateProvideLiquidityAmountBasedOnCtPrice Function
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

korok

High

# Incorrect Price Ratio Calculation in calculateProvideLiquidityAmountBasedOnCtPrice Function

## Summary

The `calculateProvideLiquidityAmountBasedOnCtPrice` function in the `MathHelper` library incorrectly adds the price ratio to 1e18 instead of multiplying, leading to incorrect liquidity calculations.

## Vulnerability Detail

In the function:

```solidity
function calculateProvideLiquidityAmountBasedOnCtPrice(uint256 amountra, uint256 priceRatio)
    external
    pure
    returns (uint256 ra, uint256 ct)
{
    ct = (amountra * 1e18) / (priceRatio + 1e18);
    ra = (amountra - ct);

    assert((ct + ra) == amountra);
}
```

The calculation `(priceRatio + 1e18)` should be `(priceRatio * 1e18)`.


## Impact

This error leads to incorrect calculations of CT and RA amounts for liquidity provision. The impact becomes more severe as the price ratio increases, potentially leading to significant miscalculations in liquidity pools and unfair asset distributions.


## Proof of Concept

Consider a scenario where:
- `amountra` = 1000e18
- `priceRatio` = 2e18 (representing a price of 2.0)

Current implementation:
```solidity
ct = (1000e18 * 1e18) / (2e18 + 1e18) = 333.33e18
ra = 1000e18 - 333.33e18 = 666.67e18
```

Correct implementation:
```solidity
ct = (1000e18 * 1e18) / (2e18 * 1e18) = 500e18
ra = 1000e18 - 500e18 = 500e18
```

The current implementation incorrectly allocates more to RA and less to CT than it should.


## Code Snippet

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/db23bf67e45781b00ee6de5f6f23e621af16bd7e/Depeg-swap/contracts/libraries/MathHelper.sol#L31

## Tool used

Manual Review

## Recommendation

Change the calculation to:

```solidity
ct = (amountra * 1e18) / (priceRatio * 1e18);
```

This will ensure the correct ratio of CT to RA is calculated based on the provided price ratio.

Duplicate of #119
sakshamguruji - Locked RA Not Incremented In Repurchase Leads To Lesser Accrued Ra For CT Redemptions
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

sakshamguruji

High

# Locked RA Not Incremented In Repurchase Leads To Lesser Accrued Ra For CT Redemptions

## Summary

Users can repurchase in the PSM where PA and DS are repurchased by providing RA . But the provided RA is not accounted for locked RA leading to lesser accrued PA when CT is redeemed after expiry.
 
## Vulnerability Detail

1.) When user deposits the locked RA increases ->

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

2.) When user redeems locked amount decreases ->

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

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

3.) Users can also repurchase where they provide the redeemed RA by providing DS + PA in the PSM , but the RA amount has not been locked , 

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

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

Therefore after expiry when users can redeem their CT and `_separateLiquidity` is invoked ->

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

lesser amount of RA will be accounted for as `availableRA` ->

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

Meaning less RA will be accrued overall by the CT tokens.

## Impact

Incorrect accounting of the RA tokens leads incorrect amount redemptions.

## Code Snippet

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

## Tool used

Manual Review

## Recommendation

The provided RA in the repurchase mechanism should be locked.

Duplicate of #126
0x73696d616f - Attackers will steal the reserve from the `Vault` by receiving `ra` in `FlashSwapRouter::__swapDsforRa()`
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0x73696d616f

High

# Attackers will steal the reserve from the `Vault` by receiving `ra` in `FlashSwapRouter::__swapDsforRa()`

### Summary

[FlashSwapRouter::__swapDsforRa()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L288) is called as part of [FlashSwapRouter::_swapRaforDs()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L98) [whenever](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L124) the reserve is sold and the resulting `Ra` is used to provide liquidity to the `Vault` by calling [Vault::provideLiquidityWithFlashSwapFee()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L125). 

However, if we follow the code, `FlashSwapRouter::__swapDsforRa()` calls [FlashSwapRouter::__flashSwap()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L301), which calls [UniswapV2Pair::swap()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L335). Then, the Uniswap pair calls [uniswapV2Call()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L338), which calls [FlashSwapRouter::__afterFlashswapSell()](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L353) and sends the `Ra` to the [caller](https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L404).

Thus, the vault is providing a fee, but does not use the acquired funds to fund it, but the already existing `Ra` in the contract. The `Ra` meant for the liquidity fee is sent to the user calling `FlashSwapRouter::_swapRaforDs()`.

### Root Cause

In `FlashSwapRouter.sol:124`, `FlashSwapRouter::__swapDsforRa()` is called, which ultimately sends the `Ra` to the `msg.sender`.

### Internal pre-conditions

None.

### External pre-conditions

None.

### Attack Path

1. User calls `swapRaforDs()` and ends up receiving `Ra` which was intended for the `Vault`.

### Impact

Users can steal all reserve from the `Vault`.

### PoC

The following code snippets show how the `Ra` ends up in the `caller`, that is, the user that calls `swapRaforDs()`.

```solidity
function __flashSwap(...) internal {
    ...
    bytes memory data = abi.encode(reserveId, dsId, buyDs, msg.sender, extraData);

    univ2Pair.swap(amount0out, amount1out, address(this), data);
}

function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
    (Id reserveId, uint256 dsId, bool buyDs, address caller, uint256 extraData) =
        abi.decod
0xNirix - Protocol will lose access to repurchased RA tokens after issue expiry, impacting CT holders.
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0xNirix

High

# Protocol will lose access to repurchased RA tokens after issue expiry, impacting CT holders.

### Summary

Lack of proper accounting for repurchased RA tokens will cause a loss of access to these tokens for the protocol after expiry, as the tokens will be stuck in the contract without being accounted for in the PSM archive.

### Root Cause

In PsmLibrary.sol, the repurchase function uses lockUnchecked instead of lockFrom
https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/libraries/PsmLib.sol#L310

```solidity
self.psm.balances.ra.lockUnchecked(amount, buyer);
```
This fails to update the internal accounting of locked RA tokens. Consequently, these tokens are not included in the locked balance. This omission has significant implications:

The convertAllToFree function, which is crucial for managing token balances during expiry, only considers the locked balance:

```solidity
function convertAllToFree(PsmRedemptionAssetManager storage self) internal returns (uint256) {
    if (self.locked == 0) {
        return self.free;
    }

    self.free = self.free + self.locked;
    self.locked = 0;

    return self.free;
}
```
Since the repurchased RA tokens are not added to locked, they are not included when convertAllToFree is called during the expiry process. This means these tokens are effectively "lost" from the accounting perspective after expiry, even though they remain in the contract.

### Internal pre-conditions

1. A repurchase transaction occurs before expiry to set unaccounted RA balance to be greater than 0.
2. The expiry event happens, triggering the separation of liquidity and creation of PSM archive

### External pre-conditions

_No response_

### Attack Path

1. User calls repurchase function before expiry
2. Contract receives RA tokens but doesn't update internal locked balance
3. Expiry occurs, triggering liquidity separation
4. PSM archive is created based on internal locked and free balances, excluding repurchased RA tokens
5. Repurchased RA tokens remain in the contract but are not accounted for in any balance or the PSM archive

### Impact

The protocol suffers a loss of access to the repurchased RA tokens after expiry. CT holders trying to redeem RA/PA after expiry will not receive their fair share of RA tokens during redemption, as some tokens are effectively stuck in the contract.


### PoC

_No response_

### Mitigation

Replace lockUnchecked with lockFrom in the repurchase function

Duplicate of #126
0xNirix - Missing RA balance update for PSM during LV redemptions will cause incorrect RA balance
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

0xNirix

High

# Missing RA balance update for PSM during LV redemptions will cause incorrect RA balance

### Summary

The failure to update RA balances in the PSM during LV redemptions will cause an incorrect (inflated) RA balance, which can result in issues such as incorrect payouts and the potential for past CT holders to drain more RA than they should be entitled to.

### Root Cause

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

 In PsmLib.sol:lvRedeemRaWithCtDs the function does not decrease the locked RA balance when LV redeems RA with CT/DS, leading to an inflated RA balance in the PSM.
```solidity
function lvRedeemRaWithCtDs(State storage self, uint256 amount, uint256 dsId) internal {
    DepegSwap storage ds = self.ds[dsId];
    ds.burnBothforSelf(amount);
    // Missing delock
}
```
The RA balance was increased during issue
```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);
    }

```
This will cause incorrect inflated payouts in redeemWithCt

```solidity
function redeemWithCt(State storage self, address owner, uint256 amount, uint256 dsId, ...) internal {
    // ...
    _separateLiquidity(self, dsId);
    
    uint256 totalCtIssued = self.psm.poolArchive[dsId].ctAttributed;
    PsmPoolArchive storage archive = self.psm.poolArchive[dsId];

    (accruedPa, accruedRa) = _calcRedeemAmount(amount, totalCtIssued, archive.raAccrued, archive.paAccrued);
    // ...
}

function _separateLiquidity(State storage self, uint256 prevIdx) internal {
    // ...
    uint256 availableRa = self.psm.balances.ra.convertAllToFree();
    // ...
    self.psm.poolArchive[prevIdx] = PsmPoolArchive(availableRa, availablePa, IERC20(ds.ct).totalSupply());
    // ...
}

// RedemptionAssetManagerLib.sol

function convertAllToFree(PsmRedemptionAssetManager storage self) internal returns (uint256) {
    if (self.locked == 0) {
        return self.free;
    }

    self.free = self.free + self.locked;
    self.locked = 0;

    return self.free;
}
```

### Internal pre-conditions

_No response_

### External pre-conditions

_No response_

### Attack Path

1. LV calls lvRedeemRaWithCtDs to redeem RA with CT/DS -- this is called at several places during liquidation of LV token
2. The PSM's RA balance remains unchanged, becoming inflated
3. Expiry occurs, triggering _separateLiquidity
4. _separateLiquidity uses the inflated RA balance to calculate availableRa
5. CT holders call redeemWithCt after expiry
6. redeemWithCt uses the inflated availableRa to calculate payouts

### Impact

The incorrect RA balance in the PSM can lead to several serious issues:

Incorrect Payouts: CT holders may receive payouts that do not accurately reflect the true amount of RA available in the system. Some users mig
vinica_boy - Wrong accounting of locked RA when repurchasing DS+PA with RA
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

vinica_boy

High

# Wrong accounting of locked RA when repurchasing DS+PA with RA

## Summary

Users have the option to repurchase DS + PA by providing RA to the PSM. A portion of the RA provided is taken as a fee, and this fee is used to mint CT + DS for providing liquidity to the AMM pair.
## Vulnerability Detail

NOTE: Currently `PsmLib.sol` incorrectly uses `lockUnchecked()` for the amount of RA provided by the user. As discussed with sponsor it should be `lockFrom()` in order to account for the RA provided.

After initially locking the RA provided, part of this amount is used to provide liquidity to the AMM via `VaultLibrary.provideLiquidityWithFee()`

```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
        // @audit-issue shouldnt it be lock checked, deposit -> redeemWithDs -> repurchase - locked would be 0
        self.psm.balances.ra.lockUnchecked(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);
    }
```

`provideLiqudityWithFee` internally uses `__provideLiquidityWithRatio()` which calculates the amount of RA that should be used to mint CT+DS in order to be able to provide liquidity.
```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);
    }
```

`__provideLiquidity()` uses `PsmLibrary.unsafeIssueToLv()` to account the RA locked.
```solidity
function __provideLiquidity(
        State storage self,
        uint256 raAmount,
        uint256 ctAmount,
        IDsFlashSwapCore flashSwapRouter,
        address ctAddress,
        IUniswapV2Router02 ammRouter,
        uint256 dsId
    ) inte
Smacaud - Incorrect token transfer breaks core flash swap mechanism
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

Smacaud

Medium

# Incorrect token transfer breaks core flash swap mechanism

## Summary

The `__afterFlashswapSell` function in the RouterState contract incorrectly handles flash loan repayments

## Vulnerability Detail

The `__afterFlashswap`  function incorrectly sends the flash loan repayment amount (raAttributed) to the caller (the swap initiator) instead to msg.sender (Flash loan provider). 

The caller, who is supposed to be sent the swap profit (received - raAttributed), instead receives the repayment amount (raAttributed) meant to repay the flash loan provider.

The implementation wrongly sets repaymentAmount = received - raAttributed) which instead should be repaymentAmount = raAttributed.  Also it swapped what should be for msg.sender and caller interchangeably. 

This breaks for the core of the Flash swap mechanism in UniswapV2. 

In Uniswap V2 flash swap, the pair contract checks its token balances before and after the swap to ensure it has received the correct repayment. When this check fails , the entire transaction will reverts

This will make the  `__afterFlashswap` to always reverts as the incorrect amount is being sent as repaymentAmount to the flash loan provider.

## Impact

This vulnerability breaks the core of the flash swap mechanism, as every attempts of the user to call this function will always fail.

## Code Snippet

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

## Tool used
Manual Review

## Recommendation

Change the __afterFlashswapSell function to:

```diff

function __afterFlashswapSell(
    ReserveState storage self,
    uint256 ctAmount,
    Id reserveId,
    uint256 dsId,
    address caller,
    uint256 raAttributed
) internal {
    // ...

    (uint256 received,) = psm.redeemRaWithCtDs(reserveId, ctAmount);

-   uint256 repaymentAmount = received - raAttributed;
+   uint256 repaymentAmount = raAttributed; 
+   repaymentAmount += 1;  // satisfy uniswapv2 rules
    Asset ra = assetPair.ra;
+  assert(repaymentAmount + raAttributed >= received);
+  IERC20(ra).safeTransfer(caller, received - raAttributed);  // send difference to user
+  IERC20(ra).safeTransfer(msg.sender, repaymentAmount); // repay the flashloan
}

```

This change ensures that the flash loan provider (msg.sender) receives the correct repayment amount, and the caller receives the profit from the swap.


Duplicate of #161
vinica_boy - Wrong accounting of locked RA when redeeming RA with DS
Audit: 2024-08-cork-protocol-judging
Severity: HIGH

vinica_boy

High

# Wrong accounting of locked RA when redeeming RA with DS

## Summary

Users have the option to redeem RA by providing DS + PA to the PSM. When they redeem RA, a fee is applied to the amount of RA redeemed, and this fee is used to mint CT for providing liquidity to the AMM pair. However, during this process, the protocol fails to correctly account for the amount of RA that remains locked in the PSM.

## Vulnerability Detail

In the current implementation of `Psm.redeemRaWithDs()`, two key operations are performed:

1. Sending the corresponding amount of RA to the user in exchange for DS + PA,
2. Providing liquidity with the fee generated from the RA redemption.

```solidity
function redeemRaWithDs(Id id, uint256 dsId, uint256 amount, bytes memory rawDsPermitSig, uint256 deadline)
        external
        override
        nonReentrant
        onlyInitialized(id)
        PSMWithdrawalNotPaused(id)
    {
        State storage state = states[id];
        // gas savings
        uint256 feePrecentage = psmBaseRedemptionFeePrecentage;

        (uint256 received, uint256 _exchangeRate, uint256 fee) =
            state.redeemWithDs(_msgSender(), amount, dsId, rawDsPermitSig, deadline, feePrecentage);

        VaultLibrary.provideLiquidityWithFee(state, fee, getRouterCore(), getAmmRouter());

        emit DsRedeemed(id, dsId, _msgSender(), amount, received, _exchangeRate, feePrecentage, fee);
    }
```

In`PsmLib._afterRedeemWithDs()` the amount unlocked is equal to the converted RA (from DS + PA) minus the fee.
```solidity
function _afterRedeemWithDs(
        State storage self,
        DepegSwap storage ds,
        address owner,
        uint256 amount,
        uint256 feePrecentage
    ) internal returns (uint256 received, uint256 _exchangeRate, uint256 fee) {
        IERC20(ds._address).transferFrom(owner, address(this), amount);

        _exchangeRate = ds.exchangeRate();
        received = MathHelper.calculateRedeemAmountWithExchangeRate(amount, _exchangeRate);

        // @audit-issue incorrect order of params
        fee = MathHelper.calculatePrecentageFee(received, feePrecentage);
        received -= fee;

        IERC20(self.info.peggedAsset().asErc20()).safeTransferFrom(owner, address(this), amount);

        // @audit wrong accounting of locked value
        self.psm.balances.ra.unlockTo(owner, received);
    }
```

The amount that is unlocked is the (converted RA from DS+PA) - fee.

The `VaultLib.__provideLiquidity()` function is used to send RA and CT tokens derived from the redemption fee to the AMM pair.
```solidity
function __provideLiquidity(
        State storage self,
        uint256 raAmount,
        uint256 ctAmount,
        IDsFlashSwapCore flashSwapRouter,
        address ctAddress,
        IUniswapV2Router02 ammRouter,
        uint256 dsId
    ) internal {
        // no need to provide liquidity if the amount is 0
        if (raAmount ==