xanoutas/solidity-security-findings huggingface dataset:
Chainlink oracle does not implement check against min price.
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PriceFeed.sol#L46
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/PositionCalculator.sol#L143


# Vulnerability details


### Background
- There is a function in the `PriceFeed` contract for a given pair that returns the square root of the baseToken price quoted in quoteToken.

```solidity
function getSqrtPrice() external view returns (uint256 sqrtPrice) {
  (, int256 quoteAnswer,,,) = AggregatorV3Interface(_quotePriceFeed).latestRoundData();

  IPyth.Price memory basePrice = IPyth(_pyth).getPriceNoOlderThan(_priceId, VALID_TIME_PERIOD);

  require(basePrice.expo == -8, "INVALID_EXP");
  require(quoteAnswer > 0 && basePrice.price > 0);

  uint256 price = uint256(int256(basePrice.price)) * Constants.Q96 / uint256(quoteAnswer);
  price = price * Constants.Q96 / _decimalsDiff;

  sqrtPrice = FixedPointMathLib.sqrt(price);
}
```

- This function is called by `getSqrtIndexPrice()` in the `PositionCalculator` whenever a price feed is available.

```solidity
function getSqrtIndexPrice(DataType.PairStatus memory pairStatus) internal view returns (uint256 sqrtPriceX96) {
  if (pairStatus.priceFeed != address(0)) {
      return PriceFeed(pairStatus.priceFeed).getSqrtPrice();
  } else {
      return UniHelper.convertSqrtPrice(
          UniHelper.getSqrtTWAP(pairStatus.sqrtAssetStatus.uniswapPool), pairStatus.isQuoteZero
      );
  }
}
```

- This function is, in turn, called by the function with the same name in the `PredyPool` contract and by the internal `calculateMinMargin()` function in the `PositionCalculator`.

1. **PredyPool's `getSqrtIndexPrice()` Function**

  ```solidity

  function getSqrtIndexPrice(uint256 pairId) external view returns (uint256) {
      return PositionCalculator.getSqrtIndexPrice(globalData.pairs[pairId]);
  }

  ```

  This function is called by:
  - The `autoHedge()` and `autoClose()` routines in the `GammaTradeMarket` contract to perform actions like hedging or closing positions based on the position price.
  - The `predyTradeAfterCallback()` function in the `PrepMarketV1` contract, via the internal `_calculateInitialMargin()` routine, to calculate the margin amount that users need to provide when executing orders.

2. **PositionCalculator's Internal `calculateMinMargin()` Function**

  ```solidity

  function calculateMinMargin(
      DataType.PairStatus memory pairStatus,
      DataType.Vault memory vault,
      DataType.FeeAmount memory feeAmount
  ) internal view returns (int256 minMargin, int256 vaultValue, bool hasPosition, uint256 sqrtOraclePrice) {
      int256 minValue;
      uint256 debtValue;

      sqrtOraclePrice = getSqrtIndexPrice(pairStatus);

      (minValue, vaultValue, debtValue, hasPosition) = calculateMinValue(
          vault.margin,
          getPositionWithFeeAmount(vault.openPosition, feeAmount),
          sqrtOraclePrice,
          pai
Incorrect prices will be returned for negative ticks.
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L35-L75


# Vulnerability details

## Impact
If `tickCumulatives[1] - tickCumulatives[0] is negative and ((tickCumulatives[1] - tickCumulatives[0]) % ago != 0`, then returned tick will be bigger than it should be.

## Proof of Concept
In `UniHelper` library function [getSqrtTWAP()](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L20-L22) used to get square root of time weighted average price.

The problem is that if `tickCumulatives[1] - tickCumulatives[0]` is negative, then timeWeightedTick should be rounded down, as is done in the [Uniswap library](https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/OracleLibrary.sol#L36)

As a result, if `tickCumulatives[1] - tickCumulatives[0]` is negative and `(tickCumulatives[1] - tickCumulatives[0]) % ago != 0`, the tick will be bigger than it should be. This opens the possibility for arbitrage opportunities.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Tick should be rounded down in this case:
```solidity
int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int256(ago)));
// @audit Always round to negative infinity
if ((tickCumulatives[1] - tickCumulatives[0]) < 0 && ((tickCumulatives[1] - tickCumulatives[0]) % ago != 0)) tick--;
```





## Assessed type

Uniswap
The `PredyPool.sol#initialize()` function is vulnerable to front running attack.
Audit: 2024-05-predy-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/PredyPool.sol#L70-L75


# Vulnerability details

## Impact
The following problems may occur due to lack of access control in the `PredyPool.sol#initialize()` function.
1.An attacker can set the `uniswapFactory` address to an invalid address.
2.An attacker can hijack `operator` privileges.

## Proof of Concept
`PredyPool.sol` implements openzeppelin's `Initializable` model. The problem is that it's unprotected from an attacker initializing the contract. The uninitialized contract can be taken over by the attacker for example by front-running the original deployer's `intialize()` call.

[See OpenZeppelin's comment in their documentation:](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializers)

>Do not leave an implementation contract uninitialized. An uninitialized implementation contract can be taken over by an attacker, which may impact the proxy. To prevent the implementation contract from being used, you should invoke the _disableInitializers function in the constructor to automatically lock it when it is deployed:

```solidity
    function initialize(address uniswapFactory) public initializer {
        __ReentrancyGuard_init();
        AddPairLogic.initializeGlobalData(globalData, uniswapFactory);

        operator = msg.sender;
    }
```
As a result, an attacker can set the `uniswapFactory` address to an invalid address.
And he can also hijack `operator` privileges.
The `operator` permission can access core functionality of the protocol, such as registering a new trading pair or withdrawing protocol revenue from that pair.
```solidity
109: function registerPair(AddPairLogic.AddPairParams memory addPairParam) external onlyOperator returns (uint256) {
```
```solidity
177:    function withdrawProtocolRevenue(uint256 pairId, bool isQuoteToken) external onlyOperator {
```
Therefore, if the `operator` authority is taken away, the protocol may face a catastrophic situation in which it may become bankrupt.
This phenomenon also appeared in `GammaTradeMarket.sol` and `PerpMarketV1.sol`.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider to add _disableInitializers() function and the constructor to `PredyPool.sol`:
```solidity
    constructor() {
	    _disableInitializers();
    }
```











## Assessed type

Access Control
Stable2::calcLpTokenSupply() - The function cannot convert under certain circumstances, DoSing `calcReserveAtRatioLiquidity`
Audit: 2024-08-basin-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-basin/blob/7e08ff591df0a2ade7d5618113dda2621cd899bc/src/functions/Stable2.sol#L103


# Vulnerability details

## Impact
`calcLpTokenSupply` is used in both `calcReserveAtRatioLiquidity` and `calcReserveAtRatioSwap`, we'll focus on `calcReserveAtRatioLiquidity`.

`calcLpTokenSupply` is called inside `calcRate` here:
```solidity
function calcReserveAtRatioLiquidity(
        uint256[] calldata reserves,
        uint256 j,
        uint256[] calldata ratios,
        bytes calldata data
    ) external view returns (uint256 reserve) {
...
for (uint256 k; k < 255; k++) {
            scaledReserves[j] = updateReserve(pd, scaledReserves[j]);
            // calculate new price from reserves:
            pd.newPrice = calcRate(scaledReserves, i, j, abi.encode(18, 18));
...
```

Inside we call the function:
```solidity
function calcRate(
        uint256[] memory reserves,
        uint256 i,
        uint256 j,
        bytes memory data
    ) public view returns (uint256 rate) {
        uint256[] memory decimals = decodeWellData(data);
        uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

        // calc lp token supply (note: `scaledReserves` is scaled up, and does not require bytes).
        uint256 lpTokenSupply = calcLpTokenSupply(scaledReserves, abi.encode(18, 18));
        rate = _calcRate(scaledReserves, i, j, lpTokenSupply);
    }
```

Note that the only change to `calcLpTokenSupply` is the added revert on the last line of the function.

```solidity
function calcLpTokenSupply(
        uint256[] memory reserves,
        bytes memory data
    ) public view returns (uint256 lpTokenSupply) {
        if (reserves[0] == 0 && reserves[1] == 0) return 0;
        uint256[] memory decimals = decodeWellData(data);
        // scale reserves to 18 decimals.
        uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

        uint256 Ann = a * N * N;
        
        uint256 sumReserves = scaledReserves[0] + scaledReserves[1];
        lpTokenSupply = sumReserves;
        for (uint256 i = 0; i < 255; i++) {
            uint256 dP = lpTokenSupply;
            // If division by 0, this will be borked: only withdrawal will work. And that is good
            dP = dP * lpTokenSupply / (scaledReserves[0] * N);
            dP = dP * lpTokenSupply / (scaledReserves[1] * N);
            uint256 prevReserves = lpTokenSupply;
            lpTokenSupply = (Ann * sumReserves / A_PRECISION + (dP * N)) * lpTokenSupply
                / (((Ann - A_PRECISION) * lpTokenSupply / A_PRECISION) + ((N + 1) * dP));
            // Equality with the precision of 1
            if (lpTokenSupply > prevReserves) {
                if (lpTokenSupply - prevReserves <= 1) return lpTokenSupply;
            } else {
                if (prevReserves - lpTokenSupply <= 1) return lpTokenSupply;
            }
        }
        revert("Non convergence: calcLpTokenSupply");
    }
```

The issue here lies th
WellUpgradeable.sol will become un-upgradeable if any of the underlying tokens is migrated to another address
Audit: 2024-08-basin-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-basin/blob/7e08ff591df0a2ade7d5618113dda2621cd899bc/src/WellUpgradeable.sol#L79-L84


# Vulnerability details

### Impact

Contract will not be upgradeable, causing the protocol to miss out on the potential new features of the newly migrated ERC20 token in use beacause attempts to use the new token during well upgrade will fail, potentially forcing redeployment which defeats the purpose of the well's upgradability.

### Proof of Concept

According to the information provided in the [readme](https://github.com/code-423n4/2024-08-basin/blob/main/README.md#general-questions), the protocol will be working with all ERC20 tokens.

> Scoping Q & A

> //...

> ERC20 used by the protocol	Any (all possible ERC20s)


Lots of tokens currently in use in the DeFi space exist in versions in which the tokens are migrated from a previous version to another. This often involves, creating a new token contract with a different address and updating and introducing new characteristics and so on. This is slightly similar to token upgrades, but in this case, tokens are un-upgradeable and for some reason or the other, the devs decide to migrate the token to a new implementation/version.
A recent example is [MATIC](https://etherscan.io/address/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0)'s current migration to [POL](https://etherscan.io/address/0x455e53CBB86018Ac2B8092FdCd39d8444aFFC3F6)which will eventually become the new, dominant token address, while leaving the previous address redundant and unmanaged by the team. Note that it doesn't necessarily involve a name change.  

Now, when the functions to upgrade the contract are called, `upgradeTo` and `upgradeToAndCall`, they both reroute to the `_authorizeUpgrade` which retrieves the tokens. The function also compares the tokens to each other ensuring that they're in the same order.

```solidity
    function _authorizeUpgrade(address newImplementation) internal view override onlyOwner {
//...
        // verify the new well uses the same tokens in the same order.
        IERC20[] memory _tokens = tokens();
        IERC20[] memory newTokens = WellUpgradeable(newImplementation).tokens();
        require(_tokens.length == newTokens.length, "New well must use the same number of tokens");
        for (uint256 i; i < _tokens.length; ++i) {
            require(_tokens[i] == newTokens[i], "New well must use the same tokens in the same order");
        }
//...
    }
```

This means that upon underlying token migration, when its being fizzled out, or when new characteristics are introduced, attempts to upgrade the well using the new token's address will fail as the check highlighted above in the `_authorizeUpgrade` function will continue to revert with the error "New well must use the same tokens in the same order", not because the tokens aren't necessarily the same, but because the addresses are now different and do not match. As a result upgrading the well to a new implementati
WellUpgradeable.sol will become un-upgradeable if any of the underlying tokens are upgraded.
Audit: 2024-08-basin-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-08-basin/blob/7e08ff591df0a2ade7d5618113dda2621cd899bc/src/WellUpgradeable.sol#L79-L84


# Vulnerability details

### Impact

Contract will not be upgradeable, causing the protocol to miss out on the potential new implementations of the newly ungraded ERC20 toke in use.

### Proof of Concept

According to the information provided in the readme, the protocol will be working with all ERC20 tokens, taking into consideration only [upgradeability behaviour in scope](https://github.com/code-423n4/2024-08-basin/blob/main/README.md#erc20-token-behaviors-in-scope).

> Scoping Q & A

> //...

> ERC20 used by the protocol	Any (all possible ERC20s)

> //...

> ERC20 token behaviors in scope

> Question	Answer

> //...

> Upgradeability	In scope

Lots of tokens currently in use in the DeFi space are upgradeable, as it allows for introducing new characteristics, fix potential bugs etc, without having to go through the process of rewriting and redeploying the token. 

Upgrading a token, however changes its proxy instance address. This is important as interactions with upgradeable tokens are not done with its original contract address, but rather the address of the proxy instance in use. 

Taking USDC as our example, its address on Ethereum mainnet is [0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48). This will be the provided/required address when querying USDC address (see sites like [coinmarketcap](https://coinmarketcap.com/currencies/usd-coin/), [coingecko](https://www.coingecko.com/en/coins/usdc), metamask etc).

This address however, is the address of its last proxy instance. Digging into the [contract code](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#readProxyContract), we'll discover that the actual toke address is [0x43506849d7c04f9138d1a2050bbf3a0c054402dd](https://etherscan.io/address/0x43506849d7c04f9138d1a2050bbf3a0c054402dd#code). Also, we can see that there had been an instance at address [0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf#code). 

Now, when the functions to upgrade the contract are called, `upgradeTo` and `upgradeToAndCall`, they both reroute to the `_authorizeUpgrade` which retrieves the tokens. The function also compares the tokens to each other ensuring that they're in the same order.

```solidity
    function _authorizeUpgrade(address newImplementation) internal view override onlyOwner {
//...
        // verify the new well uses the same tokens in the same order.
        IERC20[] memory _tokens = tokens();
        IERC20[] memory newTokens = WellUpgradeable(newImplementation).tokens();
        require(_tokens.length == newTokens.length, "New well must use the same number of tokens");
        for (uint256 i; i < _tokens.length; ++i) {
            require(_tokens[i] == newTokens[i], "New well must use the same tokens in th
Upgraded Q -> 3 from #556 [1706627549544]
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

Judge has assessed an item in Issue #556 as 3 risk. The relevant finding follows:

 ## [L-02] Auctions active during a `mark down` allow borrowers to payback their loan at a discount, leaking value from the protocol

### Bug Description
A loan's debt during auction represents a `snapshot` of the debt at a previous `creditMultiplier` value:

[LendingTerm::_call#L664-L667](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/LendingTerm.sol#L664-L667)
```solidity
664:        // update loan in state
665:        uint256 loanDebt = getLoanDebt(loanId);
666:        loans[loanId].callTime = block.timestamp;
667:        loans[loanId].callDebt = loanDebt;
```

As seen above, line 665 performs a call to `getLoanDebt`, which retrieve's the loan's current debt with respect to the current `creditMultiplier`:

[LendingTerm::getLoanDebt#L228-L230](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/LendingTerm.sol#L228-L230)
```solidity
 228:       uint256 creditMultiplier = ProfitManager(refs.profitManager)
 229:           .creditMultiplier();
 230:       loanDebt = (loanDebt * loan.borrowCreditMultiplier) / creditMultiplier;
```

However, during auction the debt is compared against an updated `principle`, which takes into consideration an updated `creditMultiplier`:

[LendingTerm::onBid#L751-L762](https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/LendingTerm.sol#L751-L762)
```solidity
751:        uint256 creditMultiplier = ProfitManager(refs.profitManager)
752:            .creditMultiplier();
753:        uint256 borrowAmount = loans[loanId].borrowAmount;
754:        uint256 principal = (borrowAmount *
755:            loans[loanId].borrowCreditMultiplier) / creditMultiplier;
756:        int256 pnl;
757:        uint256 interest;
758:        if (creditFromBidder >= principal) {
759:            interest = creditFromBidder - principal;
760:            pnl = int256(interest);
761:        } else {
762:            pnl = int256(creditFromBidder) - int256(principal);
```

Note that in the above code, `creditFromBidder` is `<= callDebt`, depending on when the bid takes place. If the `creditMultiplier` is updated when the loan is in auction, then the `callDebt` of the loan will be less than what the loan should actually be (considering the updated `creditMultiplier`). This allows the borrower to bid during the first phase of the auction and receive their original collateral at a discount. In addition, because the `callDebt` (`creditFromBidder`) stays the same while the `principle` increases, then the difference (`interest`) will be less that it should be (considering the updated `creditMultiplier`). 

### Impact
When a borrower's loan is in auction during a `mark down`, the borrower is able to repay their loan, and receive their collateral, at a discount. This results in the protocol collecting *less* interest from the borrower. The difference between the `interestExpected` and the `interestRecei
Vulnerability to first depositor pool frontrunning attack in PSM deployment
Audit: 2023-12-ethereumcreditguild-findings
Severity: HIGH

# Lines of code

 https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/test/proposals/gips/GIP_0.sol#L25


# Vulnerability details

high 

## Impact
The `GIP_0` contract, which is responsible for the deployment of the system, is expected to mint `100e18 gUSDC` before anyone interacts with the protocol. This is a crucial step as it prevents share price manipulation as pointed out in the contest documentation. However, it appears that these tokens are not being minted by the deployment script. This omission makes the deployed PSM vulnerable to a first depositor pool frontrunning attack.

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/test/proposals/gips/GIP_0.sol#L25

In a first depositor pool frontrunning attack, an attacker could potentially manipulate the price of the token by being the first to interact with the protocol. This could lead to significant financial loss for other users and could undermine the integrity of the entire system.

## Proof of Concept
In the current implementation, the `deploy()` function of the `GIP_0` contract does not include a step to mint `100e18 gUSDC` tokens. An attacker could exploit this by being the first to interact with the protocol after deployment, potentially manipulating the price of the token.

Here is a simplified sequence of events illustrating the issue:

1. The `GIP_0` contract is deployed, but the `100e18 gUSDC` tokens are not minted.
2. Alice, an attacker, is the first to interact with the protocol.
3. Alice manipulates the price of the token to her advantage.
4. Bob, an honest user, interacts with the protocol after Alice, and suffers financial loss due to the manipulated token price.

## Tools Used

Manual review

## Recommended Mitigation Steps
To mitigate this issue, ensure that the `100e18 gUSDC` tokens are minted by the deployer address during the deployment process. This could be achieved by adding a minting step to the `deploy()` function of the `GIP_0` contract. It's also recommended to add tests to verify that the tokens are correctly minted during deployment.


## Assessed type

Other