xanoutas/solidity-security-findings huggingface dataset:
Deposit / Withdraw with `reserveTokenSpecified` misses important checks
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
When depositing or withdrawing liquidity and specifying the amount of reserve tokens,  the protocol bypasses crucial checks ensuring the amounts locked don't become too small. This oversight allows a malicious actor to remove all but a negligible quantity of liquidity from the pool, rendering the trading pool ineffectual. With low and imbalanced liquidity, [rounding errors escalate](https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/README.md?plain=1#L120), causing losses for liquidity providers.


## Proof of Concept
When depositing or withdrawing liquidity by specifying the LP tokens, the flow enforces the function `_checkBalances` at the end of the execution.

The code enforces the following conditions:
```
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);

```

In simpler terms: The balances of x and y must exceed a specific minimum threshold, and the ratio of y/x should fall within a defined range.

This check is overlooked when specifying the amount of reserve tokens. Amounts of reserve tokens are specified when calling `depositGivenInputAmount` or `withdrawGivenOutputAmount`. Thus, it's possible to shift the pool into an insecure state that breaches the designated safety zones. The proof of concept below demonstrates a scenario where a user withdraws all but 1 Wei of token X from the pool:


```soldity
function testWithdrawAboveLimit(uint256 x0, uint256 y0, uint256 s0) public {
    vm.assume(x0 >= uint256(MIN_BALANCE));
    vm.assume(y0 >= uint256(MIN_BALANCE));
    vm.assume(s0 >= uint256(MIN_BALANCE));
    vm.assume(y0/x0 <= MAX_BALANCE_AMOUNT_RATIO);
    vm.assume(x0/y0 <= MAX_BALANCE_AMOUNT_RATIO);

    // deploy with default
    proteus = new EvolvingInstrumentedProteus(
    ABDKMath64x64.divu(695000000000000, 1e18),
    ABDKMath64x64.divu(692930500000000, 1e18),
    ABDKMath64x64.divu(695000000000000, 1e18),
    ABDKMath64x64.divu(692930500000000, 1e18),
    3 days
);

    // attempt to withdraw all (but 1 wei) of the provided x liquidity
    SpecifiedToken token = SpecifiedToken.X;
    uint256 witdrawnAmount = x0 - 1;

    try proteus.withdrawGivenOutputAmount(x0, y0, s0, witdrawnAmount, token) returns (uint256 tokenBurned) {
        if (tokenBurned < s0 && witdrawnAmount >= MIN_OPERATING_AMOUNT) {
            uint256 x1 = x0 - witdrawnAmount;
            uint256 y1 = y0;
            assertGe(x1, uint256(MIN_BALANCE), "min balance");
            assertLe(y1 / x1, MAX_BALANCE_AMOUNT_RATIO, "wrong ratio, x too small");
            assertLe(x1 / y1, MAX_BALANCE_AMOUNT_RATIO, "wrong ratio, x too large");
        }
    } catch {}
}
```

Once liq
roundedSpecifiedAmount can overflow in _swap() and lead to incorrect xf and yf values
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This can allow attackers to manipulate the price of the pool by depositing large amounts that trigger the overflow.

## Proof of Concept
The _swap() function first calculates roundedSpecifiedAmount by calling _applyFeeByRounding(). In _applyFeeByRounding(), the absolute value of the input amount is taken, fees are added to it, and then it is converted back to a signed integer.

The overflow can happen in this conversion back to int256: https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L844-L852

If roundedAbsoluteAmount exceeds int256.max, converting it to int256 will overflow and roundedAmount will be incorrect. This incorrect roundedAmount is then used to calculate xf and yf: https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L577-L583
Since xf and yf depend directly on roundedSpecifiedAmount, an overflow there leads to incorrect xf and yf.
This can allow attackers to manipulate the price of the pool by depositing large amounts that trigger the overflow.



## Tools Used
Manual
## Recommended Mitigation Steps
Add overflow checks in _applyFeeByRounding() when converting back to int256


## Assessed type

Other
applyFeeByRounding() can cause an overflow or underflow in roundedSpecifiedAmount resulting to incorrect swap amounts and loss of assets for liquidity providers
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This can lead to incorrect swap amounts and loss of assets for liquidity providers.

## Proof of Concept
applyFeeByRounding() can cause an overflow or underflow in roundedSpecifiedAmount if the fee calculation results in a value outside the uint256 range
The issue is that _applyFeeByRounding() takes an int256 as input, applies the fee calculation which results in a uint256, and then converts it back to an int256.
If the fee calculation results in a uint256 value that is larger than INT_MAX, converting it to int256 will overflow and wrap around to a negative value.
Similarly, if the fee calculation results in a uint256 value that is larger than INT_MAX and the input amount is negative, converting to int256 will underflow and wrap around to a large positive value.
This could result in an incorrect roundedSpecifiedAmount being passed to subsequent calculations in _swap(), leading to potential loss of funds or other issues.

Additionally speaking  
The _applyFeeByRounding() function takes in an int256 amount and applies a fee by rounding up or down. It converts the amount to an absolute uint256 value, adds/subtracts the fee percentages, and converts it back to an int256.
If the original int256 amount is very large, applying the fee percentages could cause the uint256 absoluteValue to overflow.
For example:
• amount = 115792089237316195423570985008687907853269984665640564039457584007913129639935
• absoluteValue = 115792089237316195423570985008687907853269984665640564039457584007913129639935
• Adding the fee percentage overflows absoluteValue
Similarly, if the original amount is very negative, applying the fee percentages could underflow the uint256 to 0.
This would result in roundedAmount being incorrect after converting back from uint256 to int256.



## Tools Used
Manual
## Recommended Mitigation Steps 
Add overflow/underflow checks when converting between int256 and uint256:


## Assessed type

Other
Rounding and Fee Calculation Issues in EvolvingProteus Contract
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Description:
The provided code seems to be a Solidity smart contract implementing a curve-based liquidity pool. There is a bug in the _getUtility function, specifically in the calculation of the discriminant (disc) for the quadratic formula used to calculate the utility. The incorrect handling of a and b values in the quadratic formula calculation leads to inaccurate results, and the contract could fail to work as intended.

## Affected Function:


function _getUtility(int256 x, int256 y) internal view returns (int256 utility) {
    int128 a = config.a();
    int128 b = config.b();

    int128 two = ABDKMath64x64.divu(uint256(2 * MULTIPLIER), uint256(MULTIPLIER));
    int128 one = ABDKMath64x64.divu(uint256(MULTIPLIER), uint256(MULTIPLIER));

    int128 aQuad = (a.mul(b).sub(one));  // Incorrect calculation
    int256 bQuad = (a.muli(y) + b.muli(x));
    int256 cQuad = x * y;

    int256 disc = int256(Math.sqrt(uint256((bQuad**2 - (aQuad.muli(cQuad)*4)))));
    int256 r0 = (-bQuad*MULTIPLIER + disc*MULTIPLIER) / aQuad.mul(two).muli(MULTIPLIER);
    int256 r1 = (-bQuad*MULTIPLIER - disc*MULTIPLIER) / aQuad.mul(two).muli(MULTIPLIER);

    if (a < 0 && b < 0) utility = (r0 > r1) ? r1 : r0;
    else utility = (r0 > r1) ? r0 : r1;

    if (utility < 0) revert CurveError(utility);
}
## Bug Description:
In the calculation of the discriminant disc, the term aQuad is being calculated as (a.mul(b).sub(one)), which is not correct. The correct calculation should be (a.mul(a).sub(one)), as a is the square root of the inverse of y's instantaneous price.

## Impact:
The incorrect calculation in the _getUtility function could lead to incorrect utility values and cause the contract to behave unexpectedly. This could result in inaccurate swapping, depositing, and withdrawing of tokens, impacting the overall functionality of the liquidity pool.
## Solution:
Replace the line int128 aQuad = (a.mul(b).sub(one)); with int128 aQuad = (a.mul(a).sub(one));.


function _getUtility(int256 x, int256 y) internal view returns (int256 utility) {
    int128 a = config.a();
    int128 b = config.b();

    int128 two = ABDKMath64x64.divu(uint256(2 * MULTIPLIER), uint256(MULTIPLIER));
    int128 one = ABDKMath64x64.divu(uint256(MULTIPLIER), uint256(MULTIPLIER));

    int128 aQuad = (a.mul(a).sub(one));  // Corrected calculation
    int256 bQuad = (a.muli(y) + b.muli(x));
    int256 cQuad = x * y;

    int256 disc = int256(Math.sqrt(uint256((bQuad**2 - (aQuad.muli(cQuad)*4)))));
    int256 r0 = (-bQuad*MULTIPLIER + disc*MULTIPLIER) / aQuad.mul(two).muli(MULTIPLIER);
    int256 r1 = (-bQuad*MULTIPLIER - disc*MULTIPLIER) / aQuad.mul(two).muli(MULTIPLIER);

    if (a < 0 && b < 0) utility = (r0 > r1) ? r1 : r0;
    else utility = (r0 > r1) ? r0 : r1;

    if (utility < 0) revert CurveError(utility);
}


## Tools used

Manual Review 


## Recommended
Multiply an additional time by MULTIPLIER in both _getPointGivenXandUtility and _getPointGivenYandUtility functions.
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Multiplying an additional time in f_1 will cause the trader to swap more tokens than expected.

## Proof of Concept
``
(x/utility + a)(y/utility + b) = 1
``
According to the formula provided, taking `_getPointGivenXandUtility` as an example, both `a` and `b` have already been multiplied by MULTIPLIER in the `a_convert` and `b_convert` functions respectively. The MULTIPLIER for `x` and `y` is handled in the following expressions: 

- `f_0` represents `MULTIPLIER * x / utility + a`
- `f_1` represents `MULTIPLIER * y / utility`, where the equation is derived as follows:
  ```
  MULTIPLIER * y / utility + b = MULTIPLIER / f_0
  MULTIPLIER * y / utility = MULTIPLIER / f_0 - b
  ```
- `f_2` represents `y`, and the equation can be derived as:
  ```
  MULTIPLIER * y / utility = f_1
  MULTIPLIER * y = f_1 * utility
  y = f_1 * utility / MULTIPLIER
  ```

Note that in the code, `f_1` is additionally multiplied by MULTIPLIER.
https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L751



## Assessed type

Context
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L770-L787
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
- Incorrect token pricing for swaps, deposits, withdrawals
- Allowing trades that should fail or disallowing trades that should succeed
- Pool balances diverging from the curve over time

## Proof of Concept
The potential issue is with this line:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L782
The division operation in _getPointGivenYandUtility() could potentially underflow and lead to an incorrect value for f_1.
Here is why:
f_0 is calculated as: f_0 = (( y0 * MULTIPLIER ) / utility) + b_convert;
Since y0 and utility are int256 variables, f_0 could potentially be a very small number, close to 0.
Then when doing:
f_1 = ( ((MULTIPLIER)*(MULTIPLIER) / f_0) - a_convert );
Dividing a large number like MULTIPLIER * MULTIPLIER by a very small f_0 could cause an integer underflow and f_1 to become 0, when it should actually be a large number. This would then propagate and lead to an incorrect value for f_2 and ultimately an incorrect y coordinate returned.


## Tools Used
Manual 
## Recommended Mitigation Steps 
Check for potential underflow before doing the division


## Assessed type

Other
division operation in _getPointGivenXandUtility() can underflow and lead to an incorrect value for f_1
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Tt can  allow attackers to drain funds from the pool by swapping in a way that underflows f_1.

## Proof of Concept
The division operation in _getPointGivenXandUtility() could potentially underflow and lead to an incorrect value for f_1
Here is why:
• MULTIPLIER is defined as 1e18. This is a very large number.
• f_0 comes from this calculation:
f_0 = ((( x0 * MULTIPLIER ) / utility) + a_convert);
• Since MULTIPLIER is so large, f_0 could end up being a very small number, even 0.
• Then when doing:
f_1 = ((MULTIPLIER * MULTIPLIER / f_0) - b_convert);
• Dividing MULTIPLIER * MULTIPLIER (a very large number) by a very small f_0 could underflow and wrap around to a small number.
• This would assign an incorrect and likely small value to f_1.


## Tools Used
Manual
## Recommended Mitigation Steps 
add a check that f_0 is greater than some minimum threshold, like 1e10. And if f_0 is less, throw an error or handle it in some way. Also, this can be prevented by scaling f_0 before division or using a library like SafeMath


## Assessed type

Other
performing the square operation on bQuad will result in an incorrect value for disc.
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The disc will be computed incorrectly, which then propagates to an incorrect value for the utility u. An incorrect utility value could lead to improper pricing and losses for liquidity providers.

## Proof of Concept
Performing the square operation on bQuad without checking for overflow can potentially result in an incorrect value for disc. The issue is that bQuad is an int256, so squaring it with **2 could potentially overflow and wrap around, resulting in an incorrect value.

For example:
int256 bQuad = type(int256).max; // 9223372036854775807
bQuad**2 // overflows to -1

So if bQuad is a very large positive number close to max int256, squaring it will overflow and become a small negative number. This will result in an incorrect value for disc.


## Tools Used
Manual
## Recommended Mitigation Steps
bQuad needs to be converted to uint256 before squaring to avoid potential overflow


## Assessed type

Other
block.timestamp in the contract can allow miners/bots to front-run transactions and take arbitrage opportunities
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L68-L74
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L262
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L81-L83
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L89-L90
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L115-L116
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L123-L124


# Vulnerability details

## Impact
It allows front-running and arbitrage opportunities, which can hurt liquidity providers and honest traders using the pool.

## Proof of Concept
In the constructor, block.timestamp is used to set t_ini: https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L68-L74 and https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L262
In LibConfig library, block.timestamp is used to calculate the elapsed time and curve parameters:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L81-L83 | https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L89-L90 | https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L115-L116 | https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L123-L124
This allows miners/bots to manipulate the block.timestamp to change the curve parameters and prices for their benefit.
For example, let's say the current curve parameters based on block.timestamp result in a swap price of 1 token A for 5 tokens B.
A miner/bot could see a pending transaction to swap 10 tokens A for tokens B.
By manipulating the timestamp to a future point, the curve parameters can be changed to result in a price of 1 token A for 4 tokens B.
The miner includes this transaction in a block with the manipulated timestamp.
They will receive 10 * 4 = 40 tokens B instead of the original 10 * 5 = 50 tokens B based on the pending transaction price. So they profited 10 extra tokens B.
This is possible because the curve parameters rely solely on block.timestamp, which can be manipulated by the miner. 

In addition 
The curve parameters a and b are calculated based on the current block timestamp (see config.a() and config.b()). This means the prices and swap rates can be different depending on when a transaction is mined.

For example, say the curve is ev
MIN_BALANCE can potentially allow the pool to be drained down to a very low level
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
It allows the pool reserves to be depleted to an extremely low level.

## Proof of Concept
The MIN_BALANCE constant is set to 10^12 wei (1 pico token). This is the minimum balance that is allowed for each token in the pool reserves.
In the _checkBalances() function, the code checks: if (x < MIN_BALANCE || y < MIN_BALANCE) revert BalanceError(x,y); While MIN_BALANCE is set to 10^12 wei

This means that as long as both x and y reserves are above the MIN_BALANCE, the function will allow the balances.
So in theory, the pool could be drained down to where both x and y balances are 10^12 wei each.
For example, say the pool starts with:
• x reserve: 1,000 tokens
• y reserve: 10,000 tokens
A malicious actor could gradually drain the pool by swapping x for y in small increments. As long as each swap keeps both balances above 10^12 wei, it will be allowed.
So they could drain it down to:
• x reserve: 10^12 wei
• y reserve: 10^12 wei
This could effectively drain 99.999% of the pool liquidity.

In other words 
The _checkBalances() function enforces that the x and y reserve balances must be greater than MIN_BALANCE:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L810 while MIN_BALANCE is set to 10^12 wei:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L151 
This means the x and y reserves in the pool could potentially be drained down to 0.001 ether each.
An attacker could slowly drain the pool by:
1. Calling swapGivenInputAmount() or swapGivenOutputAmount() and only trading small amounts at a time.
2. The amount traded would need to be small enough to not trigger the _checkAmountWithBalance() validation which prevents trading tiny amounts compared to the pool balances.
3. By repeating this swap attack over time, the attacker could drain the reserves down to 0.001 ether each.



## Tools Used
Manual
## Recommended Mitigation Steps
The MIN_BALANCE could be increased to a higher amount like 10^18 or more.


## Assessed type

Other
The  AMM implementation is vulnerable to flash loan attacks that could manipulate prices and balances.
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This arbitrage opportunity allows an attacker to drain funds from the pool by swapping at a temporary distorted price.

## Proof of Concept
The key vulnerability is that the swap, deposit, and withdraw functions rely on the current reserves (xBalance and yBalance) to calculate prices and token amounts. (For example The _swap function calculates output amounts based on current reserve).  A flash loan could borrow a huge amount of one token, call the swap/deposit/withdraw functions to manipulate prices/balances, and then repay the loan - all in one transaction.

For example, a flash loan of token Y could:

- Temporarily increase yBalance dramatically
- Call swapGivenInputAmount with a small amount of X as input.
- Due to the increased yBalance, the X to Y price will be very favorable, resulting in a large Y output amount.
- Repay the Y flash loan.


The deposit and withdraw functions could be manipulated in a similar way by briefly changing the reserves to mint/burn LP tokens at more favorable rates.

## Tools Used
Manual
## Recommended Mitigation Steps 
The contract should update the reserves only after the transaction completes successfully. This prevents the balances from being manipulated until after the swap/deposit/withdrawal occurs at the original rates.The key is to snapshot balances before the operation, do the calculations at the snapshotted balances, and only update the balances after the transaction succeeds. 


## Assessed type

Other