xanoutas/solidity-security-findings huggingface dataset:
Incorrect condition checking handling in `decodeWellData` function
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/functions/Stable2.sol#L310-L325


# Vulnerability details

## Impact
The `decodeWellData` function in the `Stable2.sol` contract contains an error that could lead to incorrect handling of token decimals. This function is very vital as it decodes the decimal information for the tokens in the well, which is used throughout the contracts such as `getScaledReserves`, `calcRate`, and others for various calculations.


## Proof of Concept
The error is located in the [`decodeWellData`](https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/functions/Stable2.sol#L310-L325) function:

```solidity
function decodeWellData(bytes memory data) public view virtual returns (uint256[] memory decimals) {
    (uint256 decimal0, uint256 decimal1) = abi.decode(data, (uint256, uint256));

    // if well data returns 0, assume 18 decimals.
    if (decimal0 == 0) {
        decimal0 = 18;
    }
    if (decimal0 == 0) {  // This should be checking decimal1
        decimal1 = 18;
    }
    // ... rest of the function
}
```

The second conditional statement incorrectly checks `decimal0` again instead of `decimal1`. As a result, `decimal1` is never set to 18 when it's 0, leading to potential mishandling of token decimals.

## Tools Used
Manual code review

## Recommended Mitigation Steps
Modify the `decodeWellData` function as follows:

```solidity
function decodeWellData(bytes memory data) public view virtual returns (uint256[] memory decimals) {
    (uint256 decimal0, uint256 decimal1) = abi.decode(data, (uint256, uint256));

    // if well data returns 0, assume 18 decimals.
    if (decimal0 == 0) {
        decimal0 = 18;
    }
    if (decimal1 == 0) {  // Changed from decimal0 to decimal1
        decimal1 = 18;
    }
    if (decimal0 > 18 || decimal1 > 18) revert InvalidTokenDecimals();

    decimals = new uint256[](2);
    decimals[0] = decimal0;
    decimals[1] = decimal1;
}
```

This change ensures that both `decimal0` and `decimal1` are correctly set to 18 if their original values are 0.
After implementing this fix, thoroughly test the contract with various token decimal configurations to ensure correct behavior across all scenarios.


## Assessed type

Context
Incorrect decimal return for token in `Stable2.sol#decodeWellData()` function
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L310-L325


# Vulnerability details

## Impact
The Stable2.sol#decodeWellData() function returns incorrect decimal values ​​for tokens, which causes incorrect calculations in critical functions of the contract. This is a critical issue.

## Proof of Concept
The `Stable2.sol#decodeWellData()` function returns a decimal array of tokens from well based on encoded input data.
```solidity
    function decodeWellData(bytes memory data) public view virtual returns (uint256[] memory decimals) {
        (uint256 decimal0, uint256 decimal1) = abi.decode(data, (uint256, uint256));

        // if well data returns 0, assume 18 decimals.
        if (decimal0 == 0) {
            decimal0 = 18;
        }
317:    if (decimal0 == 0) {
318:        decimal1 = 18;
319:    }
        if (decimal0 > 18 || decimal1 > 18) revert InvalidTokenDecimals();

        decimals = new uint256[](2);
        decimals[0] = decimal0;
        decimals[1] = decimal1;
    }
```
As you can see, when well data returns 0, the decimal of token1 is determined by `decimal0`, not `decimal1`.
Therefore, even if `decimal1` is not 0, if `decimal0` is 0, `decimal1` is determined as 18.

The `Stable2.sol#decodeWellData()` function is used for decimal calculations in several core functions within the contract.

[Stable2.sol#calcLpTokenSupply](https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L79)
```solidity
79:     uint256[] memory decimals = decodeWellData(data);
```
[Stable2.sol#calcReserve](https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L120)
```solidity
120:    uint256[] memory decimals = decodeWellData(data);
```
[Stable2.sol#calcRate](https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L159)
```solidity
159:    uint256[] memory decimals = decodeWellData(data);
```
[Stable2.sol#calcReserveAtRatioSwap](https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L181)
```solidity
181:    uint256[] memory decimals = decodeWellData(data);
```
[Stable2.sol#calcReserveAtRatioLiquidity](https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L254)
```solidity
254:    uint256[] memory decimals = decodeWellData(data);
```
As a result, the decimal of token1 may be over-evaluated, which may cause negative effects in the several core functions.

## Tools Used
Manual Review

## Recommended Mitigation Steps
It is recommended to modify the `Stable2.sol#decodeWellData()` function as follows:
```solidity
    function decodeWellData(bytes memory data) public view virtual returns (uint256[] memory decimals) {
        (uint256 decimal0, uint256 decimal1) = abi.decode(data, (uint256, uint256));

        // if well data returns 0, assume 18 decimals.
        if (decimal0 == 0) {
            decimal0 = 18;
        }
---     if (decimal0 == 0) {
+++     if (decimal1 == 0) {
            dec
Unrestricted Contract Upgrade Mechanism in ``WellUpgradeable`` Allows for Arbitrary Implementation Changes and Potential Fund Drainage
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L93-L96


# Vulnerability details

## Impact
The ``WellUpgradeable`` contract has a critical security vulnerability that allows any user to call the ``upgradeTo`` and ``upgradeToAndCall`` functions without any restrictions. This flaw enables an attacker to replace the contract's implementation with a malicious contract, potentially gaining control over the contract’s logic, including the ability to drain all user-deposited tokens from the pool.

Since the ``WellUpgradeable`` contract is designed to manage user funds within a liquidity pool, the consequences of this exploit are severe. An attacker could create a custom implementation that adds malicious functions, such as transferring all tokens to the attacker's address, effectively stealing all assets held by the contract. Moreover, the attacker could further manipulate the contract to seize ownership, allowing them to make additional unauthorized changes.

## Proof of Concept
re-paste this into the``test/WellUpgradeable.t.sol`` file at the testing suit offered by the protocol:
```
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Test, console} from "forge-std/Test.sol";
import {WellUpgradeable} from "src/WellUpgradeable.sol";
import {IERC20} from "test/TestHelper.sol";
import {WellDeployer} from "script/helpers/WellDeployer.sol";
import {MockPump} from "mocks/pumps/MockPump.sol";
import {Well, Call, IWellFunction, IPump, IERC20} from "src/Well.sol";
import {ConstantProduct2} from "src/functions/ConstantProduct2.sol";
import {Aquifer} from "src/Aquifer.sol";
import {WellDeployer} from "script/helpers/WellDeployer.sol";
import {LibWellUpgradeableConstructor} from "src/libraries/LibWellUpgradeableConstructor.sol";
import {MockToken} from "mocks/tokens/MockToken.sol";
import {WellDeployer} from "script/helpers/WellDeployer.sol";
import {ERC1967Proxy} from "oz/proxy/ERC1967/ERC1967Proxy.sol";
import {MockWellUpgradeable} from "mocks/wells/MockWellUpgradeable.sol";

contract WellUpgradeTestWalter is Test, WellDeployer {
    address proxyAddress;
    address aquifer;
    address initialOwner;
    address user;
    address mockPumpAddress;
    address wellFunctionAddress;
    address token1Address;
    address token2Address;
    address wellAddress;
    IERC20[] tokens = new IERC20[](2);
    address wellImplementation;
    address attacker = address(404);

    function setUp() public {
        // Tokens
        tokens[0] = new MockToken("BEAN", "BEAN", 6);
        tokens[1] = new MockToken("WETH", "WETH", 18);

        token1Address = address(tokens[0]);
        vm.label(token1Address, "token1");
        token2Address = address(tokens[1]);
        vm.label(token2Address, "token2");

        user = makeAddr("user");

        // Mint tokens
        MockToken(address(tokens[0])).mint(user, 10_000_000_000_000_000);
        MockToken(address(tokens[1])).mint(us
Incorrect Access Control in _authorizeUpgrade 
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L65
https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L93
https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L104


# Vulnerability details

## Impcat

The `WellUpgradeable::_authorizeUpgrade` function is internal but it used in `WellUpgradeable::upgradeTo` and `WellUpgradeable::upgradeToAndCall` and all of them  lacks proper access control, allowing unauthorized parties to perform upgrades. This can compromise the security and integrity of the contract by enabling malicious actors to deploy potentially harmful contract upgrades.

## Proof of Concept
Unauthorized parties can call the _authorizeUpgrade function, leading to unauthorized upgrades of the contract.
```solidity
function _authorizeUpgrade(address newImplmentation) internal view override {
    // No access control check => anyone can call this 
    require(address(this) != ___self, "Function must be called through delegatecall");

    address aquifer = aquifer();
    address activeProxy = IAquifer(aquifer).wellImplementation(_getImplementation());
    require(activeProxy == ___self, "Function must be called through active proxy bored by an aquifer");

    require(
        IAquifer(aquifer).wellImplementation(newImplmentation) != address(0),
        "New implementation must be a well implementation"
    );

    require(
        UUPSUpgradeable(newImplmentation).proxiableUUID() == _IMPLEMENTATION_SLOT,
        "New implementation must be a valid ERC-1967 implementation"
    );
}

```

## Tools Used

Manual code review

## Recommended Mitigations


Add an access control check to ensure that only authorized entities (e.g., the contract owner, dao ...) can call the _authorizeUpgrade function:

```
function _authorizeUpgrade(address newImplementation) internal view override {
    // Add access control check
++  require(msg.sender == owner(), "Only the owner can authorize upgrades");

    require(address(this) != ___self, "Function must be called through delegatecall");

    address aquifer = aquifer();
    address activeProxy = IAquifer(aquifer).wellImplementation(_getImplementation());
    require(activeProxy == ___self, "Function must be called through active proxy bored by an aquifer");

    require(
        IAquifer(aquifer).wellImplementation(newImplementation) != address(0),
        "New implementation must be a well implementation"
    );

    require(
        UUPSUpgradeable(newImplementation).proxiableUUID() == _IMPLEMENTATION_SLOT,
        "New implementation must be a valid ERC-1967 implementation"
    );
}

```


## Assessed type

Upgradable
Incorrect Conditional Check for Decimals Leading to Incorrect Calculations
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L317


# Vulnerability details

## Impact
In the `decodeWellData` function where the conditional check incorrectly uses `decimal0` instead of `decimal1`. This leads to returning incorrect decimals.
The `decodeWellData` is used in almost all functions leads to incorrect calculations

## Proof of Concept
In `decodeWellData` if well data returns 0,it assumes 18 , but the function checking `decimal0` instead of `decimal1` and assigning 18.
```solidity

 // if well data returns 0, assume 18 decimals.
        if (decimal0 == 0) {
            decimal0 = 18;
        }
        if (decimal0 == 0) {
            decimal1 = 18;
        }
```
But in case of decimal1 = 0 and decimal0 != 0 , this check won't assign 18 to decimal1 which returns incorrect values.

Those returned values are used in `getScaledReserves` function which sets both reserves to 18 decimals.
But in this case , it don't set the reserves to 18, this leads to incorrect calculation.

## Tools Used
Manual Review

## Recommended Mitigation Steps
```solidity

 // if well data returns 0, assume 18 decimals.
        if (decimal0 == 0) {
            decimal0 = 18;
        }
        if (decimal1 == 0) { // change the variable name
            decimal1 = 18;
        }
```



## Poc 

edit this function in  Stable2.t.sol and run .
```
function test_calcLpTokenSupply_diffDecimals() public {
        uint256[] memory reserves = new uint256[](2);
        _data = abi.encode(18, 0); // added 0 in place of 6
        reserves[0] = STATE_B_B0; // 10 USDT
        reserves[1] = STATE_B_B1; // 20 BEAN
        assertEq(_function.calcLpTokenSupply(reserves, _data), STATE_B_LP);
    }
```
This will revert 



## Assessed type

Invalid Validation
Insecure Upgrade Authorization in Minimal Proxy Environments
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/main/src/WellUpgradeable.sol#L65-L85


# Vulnerability details

The `_authorizeUpgrade` function is intended to manage `UUPS` proxy upgrades. It includes checks to ensure that upgrades are only authorized through delegate calls, through proxies managed by an `Aquifer`, and with valid implementations. However, the assumption that `address(this) != ___self` guarantees a delegate call is problematic for minimal proxies. These proxies might not meet this assumption, potentially allowing unauthorized upgrades. Furthermore, relying on assumptions about `ERC-1967` implementations could introduce vulnerabilities if minimal proxies are not properly accounted for.


## Impact
An attacker exploiting minimal proxy patterns could bypass the upgrade authorization checks, leading to unauthorized contract upgrades. This could allow the execution of arbitrary code or compromise the contract’s security.

## Tools Used
Manual Code Review 

## Recommended Mitigation Steps


## Assessed type

Upgradable
Incorrect Decimal Usage in `decodeWellData` Function Causes Miscalculation in Scaled Reserves
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L310-L325
https://github.com/code-423n4/2024-07-basin/blob/main/src/functions/Stable2.sol#L128-L142


# Vulnerability details

### Description

The `calcReserve()` function in `Stable2.sol` uses incorrect token precision values due to a bug in the `decodeWellData()` function. This issue leads to inaccurate scaling of reserves, which can result in incorrect calculations and potential downstream errors.

### Affected Code

The problem arises from the incorrect handling of token decimal values in the `decodeWellData()` function:

```solidity
// Stable2.sol#L310-L325

function decodeWellData(bytes memory data) public view virtual returns (uint256[] memory decimals) {
    (uint256 decimal0, uint256 decimal1) = abi.decode(data, (uint256, uint256));

    // If well data returns 0, assume 18 decimals.
    if (decimal0 == 0) {
        decimal0 = 18;
    }
    if (decimal0 == 0) {  // This should check decimal1 instead of decimal0.
        decimal1 = 18;
    }
    if (decimal0 > 18 || decimal1 > 18) revert InvalidTokenDecimals();

    decimals = new uint256 ;
    decimals[0] = decimal0;
    decimals[1] = decimal1;
}
```

This incorrect check can cause the `calcReserve()` function to use an inaccurate decimal value when performing calculations:

```solidity
// Stable2.sol#L128-L142

function calcReserve(
    uint256[] memory reserves,
    uint256 j,
    uint256 lpTokenSupply,
    bytes memory data
) public view returns (uint256 reserve) {
    uint256[] memory decimals = decodeWellData(data);
    uint256[] memory scaledReserves = getScaledReserves(reserves, decimals);

    // Other logic...

    for (uint256 i; i < 255; ++i) {
        uint256 prevReserve = reserve;
        reserve = _calcReserve(reserve, b, c, lpTokenSupply);

        // Scale reserve down to original precision
        if (reserve > prevReserve) {
            if (reserve - prevReserve <= 1) {
                return reserve / (10 ** (18 - decimals[j]));
            }
        } else {
            if (prevReserve - reserve <= 1) {
                return reserve / (10 ** (18 - decimals[j]));
            }
        }
    }

    revert("Convergence not found after 255 iterations");
}
```

### Recommended Mitigation

The `decodeWellData()` function should correctly check and apply the token's precision. Specifically, the check for `decimal1` should be corrected:

```solidity
if (decimal0 == 0) {
    decimal0 = 18;
}
if (decimal1 == 0) {  // Corrected check for decimal1.
    decimal1 = 18;
}
```

Additionally, ensure that `calcReserve()` scales the amounts down to the collateral token's native precision before performing any operations that depend on these values.




## Assessed type

Decimal
anyone can change the implementation contract due to no access control .
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L65
https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L93
https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L104


# Vulnerability details

## Impact
anyone can change the implementation contract due to no access control .

## Proof of Concept
The `WellUpgradeable` contract inherits from `UUPSUpgradeable` and overrides the `_authorizeUpgrade()` function. However, without adding access control to this function, anyone can call it to set the implementation contract. This lack of restriction allows an attacker to set a malicious contract as the implementation, which can then use the `selfdestruct()` function to destroy the proxy contract. This is possible because the implementation is called using delegatecall from the proxy.


```solidity
function _authorizeUpgrade(address newImplmentation) internal view override {

```
there two other functions are missing accesses control

```solidity
function upgradeTo(address newImplementation) public override {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }

    /**
     * @notice Upgrades the implementation of the proxy to `newImplementation`.
     * Calls {_authorizeUpgrade}.
     * @dev `upgradeTo` was modified to support ERC-1167 minimal proxies
     * cloned (Bored) by an Aquifer.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable override {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data, true);
    }
```
## Tools Used

Manual Review

## Recommended Mitigation Steps

Add proper access control to `_authorizeUpgrade()` function since it is used for authorization at the time of changing implementation contract address. This can be mitigated by using 'OwnableUpgradeable' of openzeppelin and add `onlyOwner` modifier to the `_authorizeUpgrade()` function so. Only owner can be authorized to upgrade the implementation address.




## Assessed type

Access Control
`WellUpgradeable` can be upgraded by anyone
Audit: 2024-07-basin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-basin/blob/7d5aacbb144d0ba0bc358dfde6e0cc913d25310e/src/WellUpgradeable.sol#L65


# Vulnerability details

## Impact
`WellUpgradeable` is an upgradeable version of the `Well` contract, inheriting from OpenZeppelin's `UUPSUpgradeable` and `OwnableUpgradeable` contracts. According to OpenZeppelin’s documentation for `UUPSUpgradeable.sol` ([here](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable:~:text=The%20_authorizeUpgrade%20function%20must%20be%20overridden%20to%20include%20access%20restriction%20to%20the%20upgrade%20mechanism.) and [here](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable-_authorizeUpgrade-address-)), the internal `_authorizeUpgrade` function must be overridden to include access restriction, typically using the `onlyOwner` modifier. This must be done to prevent unauthorized users from upgrading the contract to a potentially malicious implementation.

However, in the current implementation the `_authorizeUpgrade` function is overridden with custom logic but lacks the `onlyOwner` modifier. As a result, the `upgradeTo` and `upgradeToAndCall` methods can be invoked by any address, allowing anyone to upgrade the contract, leading to the deployment of malicious code and compromise the integrity of the contract.
## Proof of concept
The following test demonstrates that `WellUpgradeable` can be upgraded by any address, not just the owner. It is based on `testUpgradeToNewImplementation` with the difference that a new user address is created and used to call the `upgradeTo` function, successfully upgrading the contract and exposing the lack of access control.  
Paste the following test into `WellUpgradeable.t.sol`:
```solidity
function testUpgradeToNewImplementationNotOwner() public {
        IERC20[] memory tokens = new IERC20[](2);
        tokens[0] = new MockToken("BEAN", "BEAN", 6);
        tokens[1] = new MockToken("WETH", "WETH", 18);
        Call memory wellFunction = Call(wellFunctionAddress, abi.encode("2"));
        Call[] memory pumps = new Call[](1);
        pumps[0] = Call(mockPumpAddress, abi.encode("2"));
        // create new mock Well Implementation:
        address wellImpl = address(new MockWellUpgradeable());
        WellUpgradeable well2 =
            encodeAndBoreWellUpgradeable(aquifer, wellImpl, tokens, wellFunction, pumps, bytes32(abi.encode("2")));
        vm.label(address(well2), "upgradeableWell2");
        
        address user = makeAddr("user");
        vm.startPrank(user);
        WellUpgradeable proxy = WellUpgradeable(payable(proxyAddress));
        proxy.upgradeTo(address(well2));

        // verify proxy was upgraded.
        assertEq(address(well2), MockWellUpgradeable(proxyAddress).getImplementation());
        vm.stopPrank();
    }
```
## Recommended mitigation steps

Add the `onlyOwner` modifier to the `_authorizeUpgrade` function in `WellUpgradeable.sol` to restrict upgrade permissions:
```diff
+   functio