xanoutas/solidity-security-findings huggingface dataset:
View functions are vulnerable to front running attacks by miners
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Users would get worse rates than expected due to miner frontrunning.

## Proof of Concept
The view functions like swapGivenInputAmount and swapGivenOutputAmount return calculated swap values, but do not update state. A malicious miner could exploit this by the following steps:
1. User sends a transaction calling swapGivenInputAmount to preview the swap.
2. The miner sees this transaction in the mempool.
3. Before the user's transaction is mined, the miner sends their own transaction with the same input as the user, calling the actual swap function.
4. The miner's transaction will get mined first since they control block production. This allows them to get the swap output previewed by the user.
5. When the user's transaction finally gets mined, the state has already changed due to the miner's transaction, so the user gets a worse rate than previewed.


## Tools Used
Manual
## Recommended Mitigation Steps 
1. Make view functions private or internal so they cannot be called externally.
2. Use a commit-reveal scheme where user submits hash of swap params that is revealed later.
3. Check that swap parameters match last previewed values.



## Assessed type

Other
Inconsistent Scaling in Quadratic Formula Computation
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The `_getUtility` function is crucial for computing the utility of a pool based on a quadratic formula. An inconsistency in the scaling between different terms of this formula has been identified, potentially leading to inaccuracies in the discriminant's computation. This inconsistency can have significant repercussions for the contract's operation and its users.

The discriminant's potential miscalculation may result in the function returning a utility value that doesn't accurately represent the pool's value. Given the utility's probable role in the broader contract functions, such as pricing mechanisms, liquidity provisioning, and potential swaps, this inconsistency can lead to substantial financial discrepancies. Users might be presented with misleading values, leading to suboptimal or even damaging transaction decisions.

## Proof of Concept
The issue revolves around the calculation of the discriminant within the `_getUtility` function:

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

```solidity
int256 disc = int256(Math.sqrt(uint256((bQuad**2 - (aQuad.muli(cQuad)*4)))));
```

Here's a breakdown of the scaling inconsistency:

1. **bQuad^2 Scaling**:
   - [`bQuad`](https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L713) is derived from operations that result in a 64.64 fixed-point number.
   - Squaring this gives a value in 128.128 fixed-point format.
   
2. **aQuad.muli(cQuad) * 4 Scaling**:
   - [`aQuad`](https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L712) is a 64.64 fixed-point number.
   - [`cQuad`](https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L714) is a standard integer.
   - The multiplication thus yields a 64.64 fixed-point number.
   - Multiplying by 4 retains this format.

Given the above, `bQuad^2` (in 128.128 format) and `aQuad.muli(cQuad) * 4` (in 64.64 format) are not scaled equally prior to getting inputted in `Math.sqrt()`. This discrepancy in scaling can make the discriminant artificially positive even when it should be negative because `bQuad**2` will indeed be significantly and always larger than `aQuad.muli(cQuad)*4`, dodging all supposed complex (imaginary) solutions. To ensure accuracy, the terms must be on the same scale before subtraction.

## Tools Used
Manual

## Recommended Mitigation Steps
1. **Refactor for Consistent Scaling**: Before performing the subtraction operation, ensure both terms are in the same fixed-point format. This can be achieved by:
   - Explicitly scaling `cQuad` to the 64.64 format before the multiplication operation.
   - After multiplying `aQuad` and the scaled `cQuad`, adjust the result to match the 128.128 format, ensuring it's on the same scale as `bQuad^2`.
   - Only then, perform the
Incorrect logic in swapGivenInputAmount, withdrawGivenOutputAmount and withdrawGivenInputAmount 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#L295
https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L296
https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L451
https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L488


# Vulnerability details

## Impact
Direct opposite to the intended operation, breaking the swapGivenInputAmount(), withdrawGivenOutputAmount() and withdrawGivenInputAmount() functionalities and every operation that relies on the return value of these functions.

## Proof of Concept
It is required that the swapGivenInputAmount() function should check that the result after swap should not be less than 0. The swapGivenInputAmount() contains require() to ensure this condition. Same goes with withdrawGivenOutputAmount() and withdrawGivenInputAmount()

the issue lies in the require(), rather than ensuring result > 0, it requires that result should be < 0.
This is the direct opposite of the intended operation, breaking the functionality and every operation that relies on the return value of this function

## Tools Used
Manual Review

## Recommended Mitigation Steps
change the require to be greater than `>` 0
```solidity
require(result > 0);
```





## Assessed type

Invalid Validation
Swap transactions can be crafted that drain the reserves down to the MIN_BALANCE threshold without triggering the 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#L547-L554
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L810
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L151


# Vulnerability details

## Impact
• Loss of liquidity for other users
• Attacker gains disproportionate share of reserves
• Potential loss of fees for liquidity providers
• Risk of permanent depletion of reserves if drain continues


## Proof of Concept
1. In the _swap() function, the _checkBalances() call happens AFTER the swap calculations:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L547-L554 

2. The _checkBalances() function reverts if either reserve drops below MIN_BALANCE:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L810

3. MIN_BALANCE is defined as a very small amount - 10^12 when tokens have 18 decimals: https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L151

So by making multiple swaps that drain the reserves close to MIN_BALANCE without going below, an attacker could drain almost all the reserves without the _checkBalances() validation triggering.
For example, if the reserves contained 1000 tokens each originally, the attacker could swap 999 tokens at a time, draining one reserve to 1001 tokens while keeping the other around 1000. After repeating this multiple times, most of the pool's reserves will be drained



## Tools Used
Manual
## Recommended Mitigation Steps 
• Move _checkBalances() call to BEFORE swap calculations
• Tighten MIN_BALANCE threshold
• Add additional checks for low reserve thresholds prior to swaps
• Limit size of swaps as reserves become depleted



## Assessed type

Other
Owner could potentially update the curve configuration parameters in a malicious way to steal funds. 
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L216
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L10-L17
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L243-L249
https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L262


# Vulnerability details

## Impact
By changing the curve parameters, the owner could manipulate prices and steal funds

## Proof of Concept
The key parameters that define the curve are stored in the config variable, which is a Config struct. This contains:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L10-L17. These parameters determine the initial and final prices on each axis, as well as the start and end times for the curve evolution.
The config variable is set in the constructor based on the input parameters:https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L243-L249 and https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L262
However, there is no immutable storage or access controls on the config variable. This means the owner could update it later by changing the curve parameters, the owner could manipulate prices and steal funds. For example:
• Set a low py_final and high px_final to make Y tokens cheaper and steal them when swapping X tokens.
• Set a shorter duration to evolve the curve faster and benefit from price changes.
• Set t_final to a past timestamp so the final prices are reached immediately.



## Tools Used
Manual
## Recommended Mitigation Steps
config should be made immutable


## Assessed type

Other
external functions in this contract are vulnerable to reentrancy attacks
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This can lead to loss of user funds and incorrect pool pricing

## Proof of Concept
The key functions like swapGivenInputAmount, depositGivenInputAmount, etc do not check for reentrancy. They simply execute the swap or deposit logic without any checks.

This allows a malicious contract to call these functions, and while they are still executing, call them again recursively. For example when the swapGivenInputAmount function is called by the attacker contract, it will start executing the swap logic. But before it finishes, the attacker calls it again recursively. This allows the attacker to manipulate the state and balances while the original swap is still not finished.
Specifically, the attacker can:
- Drain funds by swapping multiple times before balances are updated
- Manipulate pricing by changing balances mid-swap
- Potentially cause overflows/underflows in balances

## Tools Used
Manual
## Recommended Mitigation Steps 
the contract should use a reentrancy guard like a nonReentrant modifier from OpenZeppelin


## Assessed type

Reentrancy
_checkBalances function is not properly enforced in the main external functions of this contract.
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
This poses a risk of irreversibly damaging the invariant by draining reserves. It could potentially allow attackers to extract more value from the pool than they deposited.

## Proof of Concept
The _checkBalances function (https://github.com/code-423n4/2023-08-shell/blob/c61cf0e01bada04c3d6055acb81f61955ed600aa/src/proteus/EvolvingProteus.sol#L810) checks that both x and y reserves are above the MIN_BALANCE threshold, otherwise it will revert. However, this check is only done in two places _swap function and _findFinalPoint function. It is missing from the main external functions like swapGivenInputAmount, depositGivenInputAmount etc
This means it is possible to drain the reserves below the minimum balance by repeatedly calling these external functions without the contract reverting the transactions.

For example, calling swapGivenInputAmount with the full balance of one reserve could drain that reserve to 0 without failing.

## Tools Used
Manual
## Recommended Mitigation Steps
_checkBalances call should be added to all external functions before they alter reserves


## Assessed type

Other
The value of LP token can be manipulated by the first user, which allows the attacker to dilute future liquidity providers’ shares
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
For the first minter, in depositGivenInputAmount() you can manipulate the X/Y ratio and the totalSupply LP token.
The X/Y ratio for calculating the number of LP tokens is calculated in
uint256 result = Math.mulDiv(uint256(uf), uint256(si), uint256(ui)). The dependence is linear.

An experienced attacker can create and burn all LP 1 Wei tokens. The value of the LP token can increase very quickly. As a result, 1 Wei LP token can be worth a significant amount.

Combining this with the loss of precision in calculating the number of LP tokens that will be issued to a new liquidity provider, an attacker can turn the pool into a trap that will demand a certain amount for all future liquidity providers, issuing fewer LP tokens for them.

## Proof of Concept
1. The X/Y ratio is very large.
2. totalSupply small value.
3. The attacker contributes Y asset.
4. Gets a lot of LP tokens.

## Tools Used
Manual review

## Recommended Mitigation Steps
Consider requiring a certain minimum amount of LP tokens (e.g. 1e8) for the first minter and lock some of the first minter's LP tokens by minting ~1% of the original amount to the factory address.


## Assessed type

Context
The _applyFeeByRounding() function will not work correctly with tokens with a non-standard number of decimal places
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Function _applyFeeByRounding() - Rounding and fees are equivalent concepts. 
The developers report: "the BASE_FEE and FIXED_FEE values were chosen such that round enough to cover numerical stability issues that arise from using a fixed precision math library and piecewise bonding curves".

However, the _applyFeeByRounding() function will not function properly with tokens with non-standard decimals. For example:
https://github.com/d-xo/weird-erc20#low-decimals
https://github.com/d-xo/weird-erc20#high-decimals

function _applyFeeByRounding(int256 amount, bool feeUp)
     private
     pure
     returns(int256 roundedAmount)
{
     bool negative = amount < 0 ? true : false;
     uint256 absoluteValue = negative ? uint256(-amount) : uint256(amount);
     // FIXED_FEE * 2 because we will possibly deduct the FIXED_FEE from
     // this amount, and we don't want the final amount to be less than
     // the FIXED_FEE.
     if (absoluteValue < FIXED_FEE * 2) revert AmountError();

     uint256 roundedAbsoluteAmount;
     if (feeUp) {
         roundedAbsoluteAmount =
             absoluteValue +
             (absoluteValue / BASE_FEE) +
             FIXED_FEE;
         require(roundedAbsoluteAmount < INT_MAX);
     } else
         roundedAbsoluteAmount =
             absoluteValue-
             (absoluteValue / BASE_FEE) -
             FIXED_FEE;

     roundedAmount = negative
         ? -int256(roundedAbsoluteAmount)
         : int256(roundedAbsoluteAmount);
}

The _applyFeeByRounding() function uses BASE_FEE and FIXED_FEE, which are constants, to calculate returns (int256 roundedAmount).

if (feeUp) {
     roundedAbsoluteAmount =
         absoluteValue +
         (absoluteValue / BASE_FEE) +
         FIXED_FEE;
     require(roundedAbsoluteAmount < INT_MAX);
} else
     roundedAbsoluteAmount =
         absoluteValue-
         (absoluteValue / BASE_FEE) -
         FIXED_FEE;


uint256 public constant BASE_FEE = 800;
uint256 constant FIXED_FEE = 10**9;

For example, if calculations are made for a USDC token that has 6 decimal places, an incorrect result will be obtained, which will lead to the likely loss of user funds.

For some values, the function will not work at all due to the check:
if (absoluteValue < FIXED_FEE * 2) revert AmountError();

For example: 100 USDC < 10**9 * 2. The result will be revert AmountError().

## Proof of Concept
absoluteValue = 10000 USDC (10000 * 10**6)
roundedAbsoluteAmount = 10000 * 10**6 - (10000 * 10**6 / 800) - 10**9 = 8750 * 10**6 - 10**9 = 7750 * 10**6
Commission for the amount of 10000 USDC: 22.5%

## Tools Used
Manual review

## Recommended Mitigation Steps
Check decimals() for the source token when the function is executed and adjust the fee value for it. Or take a percentage o
Deposit will not work properly with tokens with a transfer fee
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
There are tokens that charge a fee for transferring from address to address. For example: https://github.com/d-xo/weird-erc20#fee-on-transfer.

Deposit functions depositGivenInputAmount() (https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L353-L380), depositGivenOutputAmount() (https://github .com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L389-L416) are based on the incoming token count value.

function depositGivenInputAmount(
     uint256xBalance,
     uint256 yBalance,
     uint256 total Supply,
     uint256 depositedAmount,
     SpecifiedToken depositedToken
) external view returns (uint256 mintedAmount) {
     // deposit amount validations against the current balance
     require(
         depositedAmount < INT_MAX &&
             xBalance < INT_MAX &&
             yBalance < INT_MAX &&
             totalSupply < INT_MAX
     );

     int256 result = _reserveTokenSpecified(
         deposited Token,
         int256(depositedAmount),
         FEE_DOWN,
         int256(totalSupply),
         int256(xBalance),
         int256(yBalance)
     );

     // amount cannot be less than 0
     require(result > 0);
     mintedAmount = uint256(result);
}

mintedAmount is calculated based on depositedAmount and other parameters. However, if it is realistic to make a token deposit with a transfer fee, then fewer tokens will be sent to the contract address than depositedAmount. It is impossible to predict how many tokens will be credited to the account in advance.

function depositGivenOutputAmount(
     uint256xBalance,
     uint256 yBalance,
     uint256 total Supply,
     uint256mintedAmount,
     SpecifiedToken depositedToken
) external view returns (uint256 depositedAmount) {
     // lp amount of validations against the current balance
     require(
         mintedAmount < INT_MAX &&
             xBalance < INT_MAX &&
             yBalance < INT_MAX &&
             totalSupply < INT_MAX
     );

     int256 result = _lpTokenSpecified(
         deposited Token,
         int256(mintedAmount),
         FEE_UP,
         int256(totalSupply),
         int256(xBalance),
         int256(yBalance)
     );

     // amount cannot be less than 0
     require(result > 0);
     depositedAmount = uint256(result);
}

depositedAmount is calculated based on mintedAmount and other parameters. As with the depositGivenInputAmount() function, the problem is similar.

## Tools Used
Manual review

## Recommended Mitigation Steps
You need to calculate mintedAmount and depositedAmount using the difference in the balance of the address before and after the receipt of tokens.


## Assessed type

ERC20
Incorrect calculation of the formula in _checkAmountWithBalance() for tokens with a non-starting number of decimal places
Audit: 2023-08-shell-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Function _checkAmountWithBalance() limits the ratio between a starting balance and an input or output amount. The feature prevents accuracy errors when exchanging very small amounts for a very large pool. Accuracy errors can cause the pool to lose a small amount of value.

https://github.com/code-423n4/2023-08-shell/blob/main/src/proteus/EvolvingProteus.sol#L796-L801
function _checkAmountWithBalance(uint256 balance, uint256 amount)
    private
    pure
{
    if (balance / amount >= MAX_BALANCE_AMOUNT_RATIO) revert AmountError();
}

There are tokens that have decimals() != 18. For example,
https://github.com/d-xo/weird-erc20#low-decimals
https://github.com/d-xo/weird-erc20#high-decimals

For these tokens, the _checkAmountWithBalance() function will not function correctly or DOS.

## Tools Used
Manual review

## Recommended Mitigation Steps
You need to introduce getting decimals for tokens and implement the _checkAmountWithBalance() function depending on decimals().


## Assessed type

Decimal