xanoutas/solidity-security-findings huggingface dataset:
Lack of Balance Check in withdrawGivenOutputAmount() and withdrawGivenInputAmount() Functions.
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L426-L453
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L463-L490


# Vulnerability details

## Impact
The smart contract currently contains a vulnerability in the withdrawGivenOutputAmount() and withdrawGivenInputAmount() functions. These functions allow users to withdraw tokens based on specified amounts, but they lack proper checks to ensure that the withdrawal amount is within the limit of the user's deposited balance. As a result, an attacker can exploit this vulnerability to withdraw more tokens than they have deposited, potentially leading to substantial financial loss for the contract and its users.
## Proof of Concept
Attacker initiates a call to the withdrawGivenOutputAmount() function with manipulated parameters, providing a withdrawnAmount greater than their deposited balance.
The function executes without a proper balance check, allowing the attacker to withdraw tokens exceeding their deposited amount.

1.      function withdrawGivenOutputAmount(
        uint256 xBalance,
        uint256 yBalance,
        uint256 totalSupply,
        uint256 withdrawnAmount,
        SpecifiedToken withdrawnToken
    ) external view returns (uint256 burnedAmount) {
        // withdraw amount validations against the current balance
        require(
            withdrawnAmount < INT_MAX &&
                xBalance < INT_MAX &&
                yBalance < INT_MAX &&
                totalSupply < INT_MAX
        );

        int256 result = _reserveTokenSpecified(
            withdrawnToken,
            -int256(withdrawnAmount),
            FEE_UP,
            int256(totalSupply),
            int256(xBalance),
            int256(yBalance)
        );

        // amount cannot be less than 0
        require(result < 0);
        burnedAmount = uint256(-result);
    }
The same vulnerability exists in the withdrawGivenInputAmount() function, where an attacker can burn more tokens than they have deposited.
 2.     function withdrawGivenInputAmount(
        uint256 xBalance,
        uint256 yBalance,
        uint256 totalSupply,
        uint256 burnedAmount,
        SpecifiedToken withdrawnToken
    ) external view returns (uint256 withdrawnAmount) {
        // lp amount validations against the current balance
        require(
            burnedAmount < INT_MAX &&
                xBalance < INT_MAX &&
                yBalance < INT_MAX &&
                totalSupply < INT_MAX
        );

        int256 result = _lpTokenSpecified(
            withdrawnToken,
            -int256(burnedAmount),
            FEE_DOWN,
            int256(totalSupply),
            int256(xBalance),
            int256(yBalance)
        );

        // amount cannot be less than 0
        require(result < 0);
        withdrawnAmount = uint256(-result);
    }
 

## Tools Used
 Man
Incongruence in pool reserves validation
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L577-L583


# Vulnerability details

## Impact
The contract has two constants `MIN_M` and `MAX_M` which act as the minimum and maximum slopes between the reserves of the pool, that is (balance of y reserve) / (balance of x reserve). Every time the reserves change, the new reserves should be validated to be within the limit specified by these constants.

The function `_checkBalances` is used for the purpose of making the validation:

```solidity
function _checkBalances(int256 x, int256 y) private pure {
    if (x < MIN_BALANCE || y < MIN_BALANCE) revert BalanceError(x,y);
    int128 finalBalanceRatio = y.divi(x);
    if (finalBalanceRatio < MIN_M) revert BoundaryError(x,y);
    else if (MAX_M <= finalBalanceRatio) revert BoundaryError(x,y);
}
```

The issue is that the function `_reserveTokenSpecified` that is used in `depositGivenInputAmount` and in `withdrawGivenOutputAmount` does not call `_checkBalances`. The lack of validation will allow any user to deposit or withdraw an specified amount of tokens which may leave the ratio of the reserves out of the limits breaking an invariant of the protocol.

## Proof of Concept
The other functions involved in swap and lp are `_swap` and `_lpTokenSpecified` both makes the validation to the reserve changes. This validation is not present in the code of `_reserveTokenSpecified`.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Rewrite the `_reserveTokenSpecified` as:

```solidity
int256 xf;
int256 yf;
int256 ui;
int256 uf;
{
   // calculating the final price points considering the fee
   if (specifiedToken == SpecifiedToken.X) {
      xf = xi + _applyFeeByRounding(specifiedAmount, feeDirection);
      yf = yi;
      // @audit FIX
      _checkBalances(xi + specifiedAmount, yf);
   } else {
      yf = yi + _applyFeeByRounding(specifiedAmount, feeDirection);
      xf = xi;
      // @audit FIX
      _checkBalances(xf, yi + specifiedAmount);
   }
}

ui = _getUtility(xi, yi);
uf = _getUtility(xf, yf);

uint256 result = Math.mulDiv(uint256(uf), uint256(si), uint256(ui));
require(result < INT_MAX);   
int256 sf = int256(result);

// apply fee to the computed amount
computedAmount = _applyFeeByRounding(sf - si, feeDirection);
```


## Assessed type

Invalid Validation
Lack of checking balance in _reserveTokenSpecified.
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L563-L595


# Vulnerability details

## Impact
The `_checkBalance` function within the `EvolvingProteus.sol` contract serves the purpose of assessing whether the contract will remain stable after balance adjustments. This function is particularly relevant during balance changes for the contract.
```solidity
    function _checkBalances(int256 x, int256 y) private pure {
        if (x < MIN_BALANCE || y < MIN_BALANCE) revert BalanceError(x,y);
        int128 finalBalanceRatio = y.divi(x);
        if (finalBalanceRatio < MIN_M) revert BoundaryError(x,y);
        else if (MAX_M <= finalBalanceRatio) revert BoundaryError(x,y);
    }
```
The inputs x and y denote the updated balances of the Pool's X and Y tokens after a transaction. This code confirms that both token balances must exceed the MIN_BALANCE value. Additionally, the ratio y/x should fall within the range defined by MIN_M and MAX_M. 
```solidity
    function _reserveTokenSpecified(
        SpecifiedToken specifiedToken,
        int256 specifiedAmount,
        bool feeDirection,
        int256 si,
        int256 xi,
        int256 yi
    ) internal view returns (int256 computedAmount) {
        int256 xf;
        int256 yf;
        int256 ui;
        int256 uf;
        {
            // calculating the final price points considering the fee
            if (specifiedToken == SpecifiedToken.X) {
                xf = xi + _applyFeeByRounding(specifiedAmount, feeDirection);
                yf = yi;
            } else {
                yf = yi + _applyFeeByRounding(specifiedAmount, feeDirection);
                xf = xi;
            }
        }


        ui = _getUtility(xi, yi);
        uf = _getUtility(xf, yf);


        uint256 result = Math.mulDiv(uint256(uf), uint256(si), uint256(ui));
        require(result < INT_MAX);   
        int256 sf = int256(result);


        // apply fee to the computed amount
        computedAmount = _applyFeeByRounding(sf - si, feeDirection);
    }
```
```solidity
    function _swap(
        bool feeDirection,
        int256 specifiedAmount,
        int256 xi,
        int256 yi,
        SpecifiedToken specifiedToken
    ) internal view returns (int256 computedAmount) {
        int256 roundedSpecifiedAmount;
        // calculating the amount considering the fee
        {
            roundedSpecifiedAmount = _applyFeeByRounding(
                specifiedAmount,
                feeDirection
            );
        }


        int256 xf;
        int256 yf;
        // calculate final price points after the swap
        {


            int256 utility = _getUtility(xi, yi);


            if (specifiedToken == SpecifiedToken.X) {
                int256 fixedPoint = xi + roundedSpecifiedAmount;
                (xf, yf) = _findFinalPoint(
                    fixedPoint,
                    utility,
                    _getPointGivenXandUti
Final balances may be out of range due to lack of validation in `_reserveTokenSpecified()`
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L563


# Vulnerability details

## Impact
`_checkBalances()` is performed to ensure the balances of X and Y tokens are above MIN_BALANCE, and to ensure that Y/X ratio in [MIN_M; MAX_M) boundary. This check is performed in both swaps, `depositGivenOutputAmount()`, `withdrawGivenInputAmount()`. However there is no such a check in `depositGivenInputAmount()` and `withdrawGivenOutputAmount()`
That is how implemented checks can be bypassed.

## Proof of Concept
Main issue is that `_checkBalances()` is not invoked in `_reserveTokenSpecified()`. `_reserveTokenSpecified()` is used in calculations of `depositGivenInputAmount()` and `withdrawGivenOutputAmount()`

This test returns required amount of LP to withdraw whole token X reserve, paste to EvolvingProteusProperties.t.sol
```solidity
    function testPOC_withdrawGivenOutputAmount() public {
        uint256 result = DUT.withdrawGivenOutputAmount(3000e18, 1000e18, 1000 ether, 3000e18, SpecifiedToken.X);
        console.log(result);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add this check:
```solidity
    function _reserveTokenSpecified(
        SpecifiedToken specifiedToken,
        int256 specifiedAmount,
        bool feeDirection,
        int256 si,
        int256 xi,
        int256 yi
    ) internal view returns (int256 computedAmount) {
        ...

        // apply fee to the computed amount
        computedAmount = _applyFeeByRounding(sf - si, feeDirection);

+       _checkBalances(xf, yf);
    }
```


## Assessed type

Invalid Validation
Lack of Balance Validation
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L563-L595


# Vulnerability details

## Description

The pool's ratio of y to x must be within the interval `[MIN_M, MAX_M)`, which will be checked by the `_checkBalances()` function.
External view functions will call `_swap()`, `_reserveTokenSpecified()` or `_lpTokenSpecified()` functions to get the specified result.
However, `_checkBalances()` is only used in the `_swap()` and `_lpTokenSpecified()` functions. There is no balance validation for `depositGivenInputAmount()` and `withdrawGivenOutputAmount()` functions, which use `_reserveTokenSpecified()` function.

## Impact
If there's no other validation outside these two functions, user deposits/withdraws may break the invariant, i.e. the pool's ratio of y to x is outside the interval `[MIN_M, MAX_M)`.

## Proof of Concept
Add the following code in test/EvolvingProteusProperties.t.sol file EvolvingProteusProperties contract, and run `forge test --mt RatioOutsideExpectedInterval`.

```js
function testDepositRatioOutsideExpectedInterval(uint256 x0, uint256 y0, uint256 s0, uint256 depositedAmount) public {
  int128 MIN_M = 0x00000000000002af31dc461;
  uint256 INT_MAX_SQRT = 0xb504f333f9de6484597d89b3754abe9f;

  vm.assume(x0 >= MIN_BALANCE && x0 <= INT_MAX_SQRT);
  vm.assume(y0 >= MIN_BALANCE && y0 <= INT_MAX_SQRT);
  vm.assume(s0 >= MIN_BALANCE && s0 <= INT_MAX_SQRT);
  vm.assume(depositedAmount >= MIN_OPERATING_AMOUNT && depositedAmount < INT_MAX_SQRT && depositedAmount >= 2 * uint256(FIXED_FEE));
  vm.assume(y0/x0 <= MAX_BALANCE_AMOUNT_RATIO);
  vm.assume(x0/y0 <= MAX_BALANCE_AMOUNT_RATIO);
  vm.assume(int256(y0).divi(int256(x0) + int256(depositedAmount)) < MIN_M);   // breaks the invariant
  SpecifiedToken depositedToken = SpecifiedToken.X;
  
  vm.expectRevert();  // There should be at least one case that call did not revert as expected
  DUT.depositGivenInputAmount(
      x0,
      y0,
      s0,
      depositedAmount,
      depositedToken
  );
}

function testWithdrawRatioOutsideExpectedInterval(uint256 x0, uint256 y0, uint256 s0, uint256 withdrawnAmount) public {
  int128 MIN_M = 0x00000000000002af31dc461;
  uint256 INT_MAX_SQRT = 0xb504f333f9de6484597d89b3754abe9f;

  vm.assume(x0 >= MIN_BALANCE && x0 <= INT_MAX_SQRT);
  vm.assume(y0 >= MIN_BALANCE && y0 <= INT_MAX_SQRT);
  vm.assume(s0 >= MIN_BALANCE && s0 <= INT_MAX_SQRT);
  vm.assume(withdrawnAmount >= MIN_OPERATING_AMOUNT && withdrawnAmount < INT_MAX_SQRT && withdrawnAmount >= 2 * uint256(FIXED_FEE));
  vm.assume(y0/x0 <= MAX_BALANCE_AMOUNT_RATIO);
  vm.assume(x0/y0 <= MAX_BALANCE_AMOUNT_RATIO);
  vm.assume(withdrawnAmount < y0);    // no more than balance
  vm.assume((int256(y0) - int256(withdrawnAmount)).divi(int256(x0)) < MIN_M);   // breaks the invariant
  SpecifiedToken withdrawnToken = SpecifiedToken.Y;
  
  vm.expectRevert();  // There should be at least one case that call did not revert as expected
  DUT.withdrawGivenOutputAmou
_getUtility function may be vulnerable to precision loss
Audit: 2023-08-shell-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L712-L718


# Vulnerability details

## Impact
When calculating `r0` and `r1` in the _getUtility function precision loss may occur due to the use of the standard devision operator `/` instead of the devision functions of ABDKMath64x64. As the other values in the equation make use of the library's methods if the absolute value of the divisor is larger than the divident than the utility may be equal to zero.

## Proof of Concept
```
int256 r0 = (-bQuad*MULTIPLIER + disc*MULTIPLIER) / aQuad.mul(two).muli(MULTIPLIER);
int256 r1 = (-bQuad*MULTIPLIER - disc*MULTIPLIER) / aQuad.mul(two).muli(MULTIPLIER);
```

## Tools Used
Manual review
## Recommended Mitigation Steps
Use ABDKMath64x64's division functions to prevent precision loss.


## Assessed type

Math
swapGivenInputAmount checks the wrong result out
Audit: 2023-08-shell-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L295


# Vulnerability details

## Impact

Wrong check amount results in unusable function.

## Proof of Concept

swapGivenInputAmount calcaultes the value of result after calling _swap(). The check then requires that result < 0. However, the comment states that amount cannot be less than 0.
```
swapGivenInputAmount
        );
        int256 result = _swap(
            FEE_DOWN,
            int256(inputAmount),
            int256(xBalance),
            int256(yBalance),
            inputToken
        );
->      // amount cannot be less than 0 
->      require(result < 0); // contradicts the comment
```

In swapGivenOutputAmount, result must be greater than 0. 

```
swapGivenOutputAmount
        int256 result = _swap(
            FEE_UP,
            -int256(outputAmount),
            int256(xBalance),
            int256(yBalance),
            outputToken
        );


        // amount cannot be less than 0
->      require(result > 0);
->      inputAmount = uint256(result);
```

## Tools Used

VS Code

## Recommended Mitigation Steps

Make sure that the require statement in swapGivenInputAmount() is correct. 

```
 // amount cannot be less than 0
-        require(result < 0);
+        require(result > 0);
        // output amount validations against the current balance
        outputAmount = uint256(-result);
```


## Assessed type

Invalid Validation
Loss of precision due to division occurring before multiplication across multiple statements leads to lesser number of receiving tokens
Audit: 2023-08-shell-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L750
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L781


# Vulnerability details

## Impact
Swaps and Deposits work with two tokens X and Y. The computed amount of tokens on the receiving end decrease due to this multi-statement loss of precision occurring due to division before multiplication.

**Note: This finding is different from the [[L-06] bot finding](https://gist.github.com/code423n4/ae96f6f8d2dd6eb71d2cc84c85cfac91#l-06-loss-of-precision-on-division) due to two reasons:**
**1. The bot finding in the first place is not a division before multiplication issue.**
**2. This issue is due to loss of precision occurring across three statements.**

## Proof of Concept

In the equations below, 
**u = utility
ac = a_convert
bc = b_convert
M = MULTIPLIER**
(Rest of the symbols are self explanatory according to the current implementation in function [_getPointGivenYandUtility()](https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L770C14-L770C39). The following applies to [_getPointGivenXandUtility() function](https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L739) as well but we'll be talking about _getPointGivenYandUtility() function here).

The loss of precision occurs here due to division occurring before multiplication across the three statements below. If we take a look at this simplified equation (represents internal calculations of Lines 781-783) for f_2, we can see division occurs before multiplication on step 4: https://drive.google.com/file/d/1qVgf9ycl4tnCrRR1TR19WIt47YM-xHpd/view
```solidity
File: src/proteus/EvolvingProteus.sol
781:    int256 f_0 = (( y0  * MULTIPLIER ) / utility) + b_convert;
782:    int256 f_1 = ( ((MULTIPLIER)*(MULTIPLIER) / f_0) - a_convert );
783:    int256 f_2 = (f_1 * utility) / (MULTIPLIER); 
```

This leads to the return value f_2 (aka x0 in _getPointGivenYandUtility()) being smaller than expected. The deviance or loss of precision of the current three-step implementation above can be compared to the simplified equation (present on the last line of the image). 

Here is the coded POC, which compares loss of precision of the simplified equation with the current three-step implementation:

1. Add this test to the end of `EvolvingProteusProperties.t.sol` file
2. Run the following command to test: `forge test --match-test testLossOfPrecisionDifference`

```solidity
    function testLossOfPrecisionDifference() public {
        int256 utility = 200 * 10**18;
        int256 y = 1000 * 10**18; 

        vm.expectRevert();// We expect curve error but that is fine since the intention of this test is to compare the return value with that of the simplified equation for los
Lack of Deadline Protection in Key Functions Poses Potential Exploits
Audit: 2023-08-shell-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L266-L490


# Vulnerability details

## Impact
The absence of a deadline parameter in key function calls within the EvolvingProteus contract poses a significant vulnerability. Transactions that stay pending in the mempool due to outdated slippage could be executed at a much later time than initially intended by the user. This delay can result in unfavorable trade outcomes, potentially causing financial losses for both users and the protocol. Malicious validators, especially in PoS systems where block proposers are pre-known, could exploit this vulnerability. They might deliberately delay the inclusion of certain transactions to take advantage of market conditions or even manipulate them to their advantage.

## Proof of Concept
Although all key function (`swapGivenInputAmount()`, `swapGivenOutputAmount()`, `depositGivenInputAmount()`, `depositGivenOutputAmount()`, `withdrawGivenOutputAmount()`, and `withdrawGivenInputAmount()`) calls involving [swaps, LP token minting/burning](https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L266-L490) in EvolvingProteus.sol from [LiquidityPool.sol](https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/LiquidityPool.sol) have slippage protections, they fail to provide the important deadline parameter, as can been seen in the following example:

https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L272-L277 

```solidity
    function swapGivenInputAmount(
        uint256 xBalance,
        uint256 yBalance,
        uint256 inputAmount,
        SpecifiedToken inputToken
    ) external view returns (uint256 outputAmount) {
```
This omission allows transactions with outdated slippage to stay in a pending state, vulnerable to delayed execution.

A related article titled ["Why You Should Stop Using Block Timestamp as Deadline in Swaps"](https://blog.bytes032.xyz/p/why-you-should-stop-using-block-timestamp-as-deadline-in-swaps) highlights that in PoS systems, block proposers are known in advance, ranging from a minimum of 6 minutes and 24 seconds to a maximum of 12 minutes and 48 seconds. This predictability can be abused by malicious actors to exploit the lack of a deadline.

## Tools Used
Manual

## Recommended Mitigation Steps
Add a deadline parameter to each function and then check if the current block timestamp exceeds this deadline. If it does, the transaction should revert. Here's how you can refactor the functions:

Add the deadline parameter to each function:
```solidity
function swapGivenInputAmount(
    uint256 xBalance,
    uint256 yBalance,
    uint256 inputAmount,
    SpecifiedToken inputToken,
    uint256 deadline
) external view returns (uint256 outputAmount) {
    // ... rest of the code ...
}

// Add the deadline parameter to other functions similarly.
```
At the beginning of each function, check if the current block times