xanoutas/solidity-security-findings huggingface dataset:
Calls to get price from Chainlink may revert
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseOracleHub/OracleHelper.sol#L688
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseOracleHub/OracleHelper.sol#L714


# Vulnerability details

## Vulnerability Details
If calls to Chainlink oracles revert this will make all tokens that depend on Chainlink oracles experience a permanent denial of service since the feeds cannot be changed.

**[OracleHelper.sol#L19C1-L21C10](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseOracleHub/OracleHelper.sol#L19C1-L21C10)**
```
        if (priceFeed[_tokenAddress] > ZERO_FEED) {
            revert OracleAlreadySet();
        }
```

Chainlink calls may revert if the Chainlink multisig blocks access to the oracles.



## Impact
1. If calls to Chainlink fail it causes a Denial of Service for tokens that rely directly/indirectly on the Chainlink oracle.

## Tools Used
Manual Analysis

## Recommended Mitigation Steps
Consider using the Solidity `try...catch` statement to make and handle Chainlink calls, this ensures that the errors can be handled explicitly.

In the `catch` statement an alternative oracle can be used. The oracle does not need to be available before deployment of the Wiselending protocol. Clearly defining its interface allows the oracle to be added after official deployment.


## Assessed type

Access Control
Function Signature Collision (Ambiguity in Function Invocation)
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseLending.sol#L636
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WrapperHub/AaveHub.sol#L238


# Vulnerability details

## Impact
Two functions with the same name and parameter types are defined in different contracts(WiseLending.sol and AaveHub.sol), leading to ambiguity in function invocation.

When invoking a function with the shared name and parameter types, Solidity cannot uniquely identify which function to call based solely on the function signature. This ambiguity may result in unintended behavior or errors during function execution.

## Proof of Concept
 
In foundry, use

- cast sig "withdrawExactAmountETH(uint256, uint256)" 
```
0x99080e70

``

## Tools Used
Manual review  and foundry
## Recommended Mitigation Steps
To avoid ambiguity in function invocation, use distinct function names or parameter types for functions located in different contracts. Alternatively, consider varying the function's visibility or state mutability along with the parameter types to achieve function overloading if necessary.


## Assessed type

Other
Hardcoded uniswap v3 pool fee can not be the most profitable swap
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmLeverageLogic.sol#L278
https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmDeclarations.sol#L95


# Vulnerability details

## Impact
When someone enters or exits a power farm in Arbitrum chain, a swap between WETH and ENTRY_ASSET is executed:

```
        if (block.chainid == ARB_CHAIN_ID) {

            _depositAmount = _getTokensUniV3(
                _depositAmount,             // amountIn
                _getEthInTokens(
                        ENTRY_ASSET,
                        _depositAmount
                    )
                * reverseAllowedSpread
                / PRECISION_FACTOR_E18,     // amountOutMinimum
                WETH_ADDRESS,               // tokenIn
                ENTRY_ASSET                 // tokenOut
            );
        }
```
This function executes this swap in a uniswap pool with a hardcoded fee:

```
    // Uniswap fee for arbitrum
    uint24 internal constant UNISWAP_V3_FEE = 100;



    function _getTokensUniV3(
        uint256 _amountIn,
        uint256 _minOutAmount,
        address _tokenIn,
        address _tokenOut
    )
        internal
        returns (uint256)
    {
        return UNISWAP_V3_ROUTER.exactInputSingle(
            IUniswapV3.ExactInputSingleParams(
                {
                    tokenIn: _tokenIn,
                    tokenOut: _tokenOut,
                    fee: UNISWAP_V3_FEE,
                    recipient: address(this),
                    deadline: block.timestamp,
                    amountIn: _amountIn,
                    amountOutMinimum: _minOutAmount,
                    sqrtPriceLimitX96: 0
                }
            )
        );
    }
```
In this case, the uniswap fee is hardcoded to 100 which corresponds to a 0.01%.

Even though 0.01% seems the cheapest uniswap fee, it may not be the most profitable pool to execute the swap. This can be due to the pool not being intialized, or having low liquidity. Normally for pegged tokens like USDC/DAI the 0.01% fee pool should have enough liquidity to execute a swap and it will be the most profitable one. However, for different tokens the best pool/the best path to execute a swap may not be just as straightforward as choosing the lowest fee pool and executing the swap in there.

## Tools Used
Manual review

## Recommended Mitigation Steps
Adjusting the fee pool to execute the swaps should be done when creating the power farm knowing which token will be involved. Some research needs to be done to determine which fee pool can provide more outcome given the pool fee, the liquidity and impact that the swap will have into that pool.

It is also worth noting that for some tokens the best swap may be a path with intermediate tokens. For example stETH -> USDC, USDC -> WETH.


## Assessed type

Uniswap
Chainlink price validation does not check for Round completeness which may lead to stale price.
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseOracleHub/OracleHelper.sol#L556
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseOracleHub/OracleHelper.sol#L714


# Vulnerability details

## Details
The protocol uses a system to validate the prices returned from chainlink pricefeeds. It does this by checking the chainlink status through the `WiseOracleHub.chainlinkIsDead` function. This calls an internal function, the `OracleHelper._chainlinkIsDead`, which checks the round timestamp alongside confirming against a Unitwap price oracle. However, there is an important check skipped which is to check that the current round for the chainlink feed has been completed. 

## Impact
Stale price may be returned from the oracle which would affect every single aspect of the protocol requiring pricefeed data, which are some of the most significant aspects of the protocol system. Deposit, withdrawal, borrowing, lending, reward systems can be affected.

## Proof of Concept
This issue may occur if the chainlink oracle can not find a consensus to start a new round for any number of reasons (node congestion, round abandoned by chainlink oracle), causing potentially stale price to be returned to the protocol. 

## Tools Used
Manual review
## Recommended Mitigation Steps
1. Add a check to ensure the answeredInRound > roundId for the latestRoundData returned by chainlink. 
This may require altering the `OracleHelper.getLatestRoundId` to return the `latestRoundData.answeredInRound` variable as well or querying both variables and carrying out this check within `OracleHelper._chainlinkIsDead`.

```diff
  
  function _chainLinkIsDead(
        address _tokenAddress
    )
        internal
        view
        returns (bool)
    {
  ...........................................

 -     uint80 latestRoundId = getLatestRoundId(
 -        _tokenAddress
 -      );

+    (
+        int80 roundId,,,,uint80 answeredInRound
+       ) = priceFeed[_tokenAddress].latestRoundData();

+      if(answeredInRound < roundId) {
+             revert; }

        
...........................................
    }
```  


## Assessed type

Oracle
``blockReservePublic()`` cannot be reversed
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L64-L69


# Vulnerability details

## Impact

The function ``blockReservePublic()`` is used to lock reserve functionalities while minting PositionNFTS. However the main issue here is once it is locked or paused, you can never unlock/unpause it.

## Proof of Concept

```solidity
    function blockReservePublic()
        external
        onlyMaster
    {
        reservePublicBlocked = true;
    }
```

As seen above, the only variable that the ``blockReservePublic()`` can be set is true. This makes it impossible to set to false to allow NFTID reservations.

```solidity

    modifier onlyReserveRole() {
        if (reservePublicBlocked == true) {
            if (reserveRole[msg.sender] == false) {
                revert NotPermitted();
            }
        }
        _;
    }

     function reservePositionForUser(
        address _user
    )
        onlyReserveRole
        external
        returns (uint256)
    {
        return _reservePositionForUser(
            _user
        );
    }
```
## Tools Used
Manual Review


## Recommended Mitigation Steps
Add a bool parameter which will allow/block reserve from public use.




## Assessed type

DoS
ApprovalHelper, TransferHelper, and CallOptionalReturn all wrong
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/TransferHub/ApprovalHelper.sol#L7-L29
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/TransferHub/CallOptionalReturn.sol#L7-L38
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/TransferHub/TransferHelper.sol#L7-L51


# Vulnerability details

## Impact

- The ``CallOptionalReturn.sol`` provides the ``_callOptionalReturn()`` which is used to call a token contract, and return a boolean or revert based of the scenario. However it is done wrong.

```solidity
  (
            bool success,
            bytes memory returndata
        ) = token.call(
            data
        );

        bool results = returndata.length == 0 || abi.decode(
            returndata,
            (bool)
        );

        if (success == false) {
            revert();
        }

        call = success
            && results
            && token.code.length > 0;
    
```

The above code is the content of the function however,
1. if the erc20 token returns false instead of reverting from a call, this function does not revert. This is gotten as the ``bool results`` but is not checked here or where ever it is called.


- ``ApprovalHelper`` is used to perform ``safeApprove()`` call on tokens, however it misses something important.
```solidity
function _safeApprove(
        address _token,
        address _spender,
        uint256 _value
    )
        internal
    {
        _callOptionalReturn(
            _token,
            abi.encodeWithSelector(
                IERC20.approve.selector,
                _spender,
                _value
            )
        );
    }
```
It does not check the bool being returned from the ``_callOptionalReturn``

- ``TransferHelper`` is used to perform ``_safeTransfer()`` and ``_safeTransferFrom`` call on tokens, however it misses something important also. It does not check the bool being returned from the ``_callOptionalReturn``


```solidity
    function _safeTransfer(
        address _token,
        address _to,
        uint256 _value
    )
        internal
    {
        _callOptionalReturn(
            _token,
            abi.encodeWithSelector(
                IERC20.transfer.selector,
                _to,
                _value
            )
        );
    }

```


## Proof of Concept
From the EIP20 at https://eips.ethereum.org/EIPS/eip-20, it is stated that:
``
Callers MUST handle false from returns (bool success). Callers MUST NOT assume that false is never returned!
``
However, this is not handled here. 


Below is an ERC20 compliant token which tests the transfer functionality

```solidity


import {Test, console} from "forge-std/Test.sol";


interface IERC20 {

    function transferFrom(address from, address to, uint256 amount) external returns (bool);
        function approve(address spender, uint256 a
No slippage protection on LP token deposits for early users
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L682
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L529
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L452
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L502
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L334
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L386
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L114


# Vulnerability details

## **Vulnerability Details:**

The PendlePowerFarmToken contract is initialized through the [initialize](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L682)  function, during which the _totalSupply and underlyingLpAssetsCurrent are initially assigned a value of 1.

```solidity
    function initialize(
        ...
    )
        external
    {
        ...
        lastInteraction = block.timestamp;

        _totalSupply = 1;
        underlyingLpAssetsCurrent = 1;
        mintFee = 3000;
        INITIAL_TIME_STAMP = block.timestamp;
    }
```

Users can make deposits into the LP through the [depositExactAmount](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L529) function, which relies on the [previewMintShares](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmToken.sol#L452) function to calculate the number of shares a user will receive based on the specified _underlyingLpAssetAmount. The calculation of shares is performed using the following formula:

- previewMintShares = _underlyingAssetAmount x totalSupply / _underlyingLpAssetsCurrent

Therefore, for the first depositor, given that totalSupply and _underlyingLpAssetsCurrent were initially set to one, it might seem that they would receive a one-to-one ratio. However, this initial assumption could be misleading and may result in the user receiving a less favorable rate.

The contract includes an [addCompoundRewa
Same address set for Curve and Uniswap 
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmDeclarations.sol#L196-L202


# Vulnerability details


## Impact
In the constructor of ``PendlePowerFarmDeclarations.sol``, both Curve and the Uniswap router are set to the same address. 
However, they have different addresses, bytecode, and available function selectors, hence calls to which ever is wrong would revert. They are also immutable, hence they cannot be modified. 

## Proof of Concept
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmDeclarations.sol#L196-L203

```solidity

        //@audit curve and uniswap have diff addresses

        CURVE = ICurve(
            _dexAddress
        );

        UNISWAP_V3_ROUTER = IUniswapV3(
            _dexAddress
        );


```


## Tools Used
Manual Review


## Recommended Mitigation Steps

Pass in the appropriate addresses as parameters  



## Assessed type

Error
``blockReservePublic()`` does not block public reservation, only third-party
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L90-L109


# Vulnerability details


## Impact

The ``blockReservePublic()`` is used to block the public reserve, however it does not actually block this. 
Reserving can be done through 2 methods:
- The ``reservePosition()`` is used for individual reservations
- The ``reservePositionForUser()`` is used for third party reservation 

```solidity
 function reservePosition()
        external
        returns (uint256)
    {
        return _reservePositionForUser(
            msg.sender
        );
    }

    function reservePositionForUser(
        address _user
    )
        onlyReserveRole
        external
        returns (uint256)
    {
        return _reservePositionForUser(
            _user
        );
    }
```

As seen above, the ``reservePosition()`` is not protected, neither is the variable checked to see if ``reservePublicBlocked`` has been set to true.
This allows indivduals to continue reserving after is is blocked.

## Proof of Concept

```solidity
 function reservePosition()
        external
        returns (uint256)
    {
        return _reservePositionForUser(
            msg.sender
        );
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Revert all calls to ``reservePosition()`` when ``reservePublicBlocked`` is true, or implement the ``onlyReserveRole`` which does the necessary checks



## Assessed type

Access Control
MAX_COLLATERAL_FACTOR not checked in WiseLending and PendlePowerFarm
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseLendingDeclaration.sol#L319
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PoolManager.sol#L116-L119


# Vulnerability details


## Impact
In the codebase, a ``MAX_COLLATERAL_FACTOR`` is created for the [WiseLending.sol](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseLendingDeclaration.sol#L319) :

```solidity
    uint256 internal constant MAX_COLLATERAL_FACTOR = 85 * PRECISION_FACTOR_E16;
```

and the [PendlePowerFarm.sol](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmDeclarations.sol#L62) :

```solidity
    // RESTRICTION VALUES

    uint256 internal constant MAX_COLLATERAL_FACTOR = 95 * PRECISION_FACTOR_E16;

```
However, this variables are not checked when modifying the ``collateralFactor`` itself, allowing the values to be set well over the max price. 
This max values are set to 85% and 95% respectively, however it is not enforced. 

## Proof of Concept
First instance of this is the ``PoolManager`` contract at https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PoolManager.sol#L116-L119. The new collateral factor is checked against 100e16, instead of 85e16, this is a large difference:
```solidity
        _validateParameter(
            _collateralFactor,
            PRECISION_FACTOR_E18 //@audit should be MAX_COLLATERAL_FACTOR
        );

```

Another instance of this the constructor of ``PendlePowerFarmDeclarations``, this also does an insufficient check. 
Although this can be categorized as protected functions, they directly break Max Collateral Factor which is a protocol invariant.


## Tools Used

Manual Review



## Recommended Mitigation Steps
Validate the new collateral factor against the individual maximum
```solidity
        _validateParameter(
            _collateralFactor,
            MAX_COLLATERAL_FACTOR
        );

```


## Assessed type

Invalid Validation
onERC721Received callback is never called when new tokens are minted. 
Audit: 2024-02-wise-lending-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PowerFarmNFTs/PowerFarmNFTs.sol#L66-L77


# Vulnerability details

## Impact
After minting is completed OnERC721Received() is not called to verify if the target address which is the msg.sender implements {IERC721Receiver-onERC721Received}.The ERC721 implementation used by the Minter Reserver contract does not properly call the corresponding callback when new tokens are minted. However, if key owner is a contract address that does not support ERC721, the NFT can be frozen in the contract as per the documentation of the EIP721. 
Therefore calling mint this way does not ensure that the receiver of the NFT is able to accept them, _safeMint() should be used with reentrancy guards as a guard to protect the user as it checks to see if a user can properly accept an NFT and reverts otherwise.

## Proof of Concept
As a result of the mintKey() not implementing the onERC71Received callback correctly when tokens are minted, it can lead to tokens being stuck in the target address if it is a contract.
```solidity
     function mintKey(
        address _keyOwner,
        uint256 _keyId
    )
        external
        onlyFarmContract
    {
        _mint(
            _keyOwner,
            _keyId
        );
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Make use of Safemint instead of mint to prevent NFTs from being stuck and also add a nonReentrant modifier so as to prevent a reentrancy possibility 


## Assessed type

ERC721