xanoutas/solidity-security-findings huggingface dataset:
An attacker can make Pendle Oracle revert, locking liquidations, deposits, withdrawals, and everything else that uses this oracle
Audit: 2024-02-wise-lending-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/1240a22a3bbffc13d5f8ae6300ef45de5edc7c19/contracts/DerivativeOracles/PendleLpOracle.sol#L112-L115


# Vulnerability details

## Summary
An attacker can update PendleOracle every block, which will fill all the observations. This will lead to the oldest observation becoming less than TWAP_TIME, which will lead to a revert with `OracleTargetTooOld`.

## Vulnerability Details
1. Oracle is set in:
	1. WiseOracleHub.`addOracle` => OracleHelper.`_addOracle` => `priceFeed[_tokenAddress]`
	2. It can be [pendleChildLpOracleInstance](https://github.com/code-423n4/2024-02-wise-lending/blob/1240a22a3bbffc13d5f8ae6300ef45de5edc7c19/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmControllerBase.t.sol#L452), as well as [ptOracleDerivativeInstance](https://github.com/code-423n4/2024-02-wise-lending/blob/1240a22a3bbffc13d5f8ae6300ef45de5edc7c19/contracts/DerivativeOracles/PtOracleDerivative.t.sol#L157) and [ptOraclePureInstance](https://github.com/code-423n4/2024-02-wise-lending/blob/1240a22a3bbffc13d5f8ae6300ef45de5edc7c19/contracts/DerivativeOracles/PtOraclePure.t.sol#L129) 
2. Oracle called in many essential functions, including:
	1. Liquidations (WiseLending.liquidatePartiallyFromTokens => WiseCore.`_coreLiquidation` => WiseOracleHub.`getTokensInETH` => WiseOracleHub.`latestResolver` => WiseOracleHub.`chainLinkIsDead` =>  OracleHelper.`_chainLinkIsDead` => OracleHelper.`_getRoundTimestamp` => PendleChildLpOracle.`getRoundData` => PendleChildLpOracle.`latestAnswer` => PendleLpOracle.`latestAnswer` => ORACLE_PENDLE_PT.`getOracleState`)
	2. Deposit
			- WiseOracleHub.`chainLinkIsDead
				 - WiseCore.`_checkDeposit`
					 - WiseCore.`_handleDeposit`
						 - WiseLending.`_depositExactAmountETH`
							 - WiseLending.`depositExactAmountETH`
							 - WiseLending.`depositExactAmountETHMint`
						 - WiseLending.`depositExactAmount`
	3. Withdraw
			- WiseOracleHub.`getTokensInETH`
				- WiseSecurityHelper.`_getTokensInEth`
					- WiseSecurityHelper.`getETHBorrow`
						- WiseSecurityHelper.`overallETHBorrowBare`
							- WiseSecurity.`checksWithdraw`
								- WiseCore.`_coreWithdrawToken`
									- WiseLending.`_handleWithdrawShares`
										- WiseLending.`withdrawExactShares`
	4.  Many-many more: borrow, recalibrate, view functions, etc.
3. How it works on Pendle
	1. Pendle will throw `Errors.OracleTargetTooOld` if we request too far in the past, e.g., the oldest observation is 10 minutes ago, but we request 30 minutes ago. See [here](https://github.com/pendle-finance/pendle-core-v2-public/blob/0e9e00b287713b5d220ada02db45bd617db31e89/contracts/core/Market/OracleLib.sol#L135-L136). Also see the PoC
	```solidity
			// ensure that the target is chronologically at or after the oldest observation
			if (target < beforeOrAt.blockTimestamp) revert Errors.OracleTargetTooOld(target, beforeOrAt.blockTimestamp);
	```
	 2. The oldest observations are overwritten with the newest ones, see
Vote rewards cannot be claimed since PendlePowerFarmController can't receive ETH
Audit: 2024-02-wise-lending-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmController.sol#L581-L597


# Vulnerability details

 PendlePowerFarmContoller is supposed to receive vote rewards from pendle which can only be raw ETH. The problem is powerfarmcontoller has no method to receive ETH therefore an attempt by the pendle vote rewards contract to send ETH to powerfarmcontroller will revert meaning vote rewards can't be claimed. Also worth noting is that pendlepowerfarmcontroller has a way to foward ETH reiterating the fact that it is supposed to receive ETH.
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarmController/PendlePowerFarmController.sol#L600-L609

## Proof of Concept
Function claimvoterewards makes an external call pendle vote rewards to claim vote rewards as shown below; 

        {function claimVoteRewards(
         uint256 _amount,
         bytes32[] calldata _merkleProof
         )
         external
         {
        PENDLE_VOTE_REWARDS.claimRetail(
            address(this),
            _amount,
            _merkleProof
         );
         }
       
address(this) being the pendlepowerfarmcontroller.

Pendle vote rewards tries to send ETH back to pendlepowerfarmcontroller.
   }

    function claimRetail(
        address receiver,
        uint256 totalAccrued,
        bytes32[] calldata proof
    ) external returns (uint256 amountOut) {
        address user = msg.sender;
        if (!_verifyMerkleData(user, totalAccrued, proof)) revert Errors.InvalidMerkleProof();

        amountOut = totalAccrued - claimed[user];
        claimed[user] = totalAccrued;

        // assert(claimed[user] <= totalAccrued); // important invariant, obviously true

        // transfer ETH last
        _transferOut(NATIVE, receiver, amountOut);
        emit Claimed(user, amountOut);
    }
The above function is from pendle and is used to claim rewards.
As you can see  its ETH which is being sent back to the receiver (pendlepowerfarmcontroller ).

##Impact
Vote rewards cannot be claimed.

## Tools Used
https://github.com/pendle-finance/pendle-core-v2-public/blob/8fe658db97579458a110565ebb7b7081b39f54ae/contracts/LiquidityMining/VeDistributor/PendleFeeDistributorV2.sol#L32-L47
## Recommended Mitigation Steps
 Recommend adding a receive () external payable function to  pendlepowerfarmcontroller.


## Assessed type

ETH-Transfer
Exiting a farm on mainnet assumes a peg of 1:1  when swapping stETH for ETH 
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#L194-L199
https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmLeverageLogic.sol#L293-L308


# Vulnerability details

## Impact
When stETH depegs from ETH, the swaps on Curve will revert due to requesting a higher amountOut than what the curves pool will give.

## Proof of Concept
When exiting a farm on mainnet, the requested tokensOut is set as `stETH` for redeeming the SY tokens on the PENDLE_SY contract. Once the PowerFarm has on its balance the `stETH` tokens, [it does a swap from stETH to ETH using the Curves protocol.](https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmLeverageLogic.sol#L293-L308)

The problem is that the implementation is assuming a peg of 1 ETH ~= 1 stETH. Even though both tokens have a tendency to keep the peg, this hasn't been always the case as it can be seen [in this dashboard](https://dune.com/LidoAnalytical/Curve-ETHstETH). There have been many episodes of market volatility that affected the price of stETH, notably the one in last June when stETH traded at ~0.93 ETH.

[When computing the `_minOutAmount`](https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/PowerFarms/PendlePowerFarm/PendlePowerFarmLeverageLogic.sol#L194-L199), the PowerFarm calculates the ethValue of the received stETH tokens by requesting the price of the `WETH` asset, and then it applies the reverAllowedSpread to finally determine the `_minOutAmount` of ETH tokens that will be accepted from the swap on Curves
- The WETH price is pegged 1:1 to ETH, 1 WETH will always be 1 ETH, but, using the price of WETH to determine the `minOutAmount` is problematic, because, as seen in the dashboard, historically, `1 stETH has deppeged from 1:1 from ETH`
  - Example: Assume a user sets a slippage of 1%. If stETH were to depeg to 0.95 per ETH, when swapping it would try to make sure that the user received at least 0.99 ETH. Whenever trying to swap it will revert because curves can't give the requested `minOutAmount` of ETH tokens, it could give at most 0.95 ETH per stETH.

```
function _logicClosePosition(
    ...
)
    private
{
    ...

    //@audit-info => When exiting a farm on mainnet, the `tokenOut` is set to be `stETH`
    address tokenOut = block.chainid == 1
        ? ST_ETH_ADDRESS
        : ENTRY_ASSET;

    ...

    uint256 ethAmount = _getEthBack(
        tokenOutAmount,
        _getTokensInETH(
            //@audit-info => When exiting a farm on mainnet, the price of the `tokenOut` is requested as if it were the price of `WETH`
            //@audit-issue => Here is where the code assumes a peg of `1 stETH to be 1 ETH`
            block.chainid == 1
                ? WETH_ADDRESS
                : ENTRY_ASSET,
            tokenOutAmount
        )
            * reverse
[M-5] Missing Important event logs after sensitive actions
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#L329-L343


# Vulnerability details

## Description:

Function is missing important event logs, sensitive actions is performed but there are no events being emitted.

## Impact:

Lack of sensitive informations by users.

## Tools Used

Manual Review

## Proof Of Concept:

```javascript
    function collateralizeDeposit(
        uint256 _nftId,
        address _poolToken

    ) external syncPool(_poolToken) {

        WISE_SECURITY.checksCollateralizeDeposit(
            _nftId,
            msg.sender,
            _poolToken
        );

        userLendingData[_nftId][_poolToken].unCollateralized = false;
    }
```

## Recommended Mitigation Steps:

Emit an eventLog upon successfull deposit

```diff
+    event DepositCollateralized(
+        address indexed sender,
+        uint256 indexed nftId,
+        address indexed token,
+        uint256 amount,
+        uint256 shares,
+        uint256 timestamp
+    );

    function collateralizeDeposit(
        uint256 _nftId,
        address _poolToken

    ) external syncPool(_poolToken) {

        WISE_SECURITY.checksCollateralizeDeposit(
            _nftId,
            msg.sender,
            _poolToken
        );

        userLendingData[_nftId][_poolToken].unCollateralized = false;

+       _emitDepositCollateralized(msg.sender, _nftId, _poolToken);
    }

```


## Assessed type

Context
[M-4] Misinformation of events for critical parameters
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#L600-L628


# Vulnerability details

## Description:

Event is currently being emitted before the transfer has occured. This leads to misinformation of critical parameters

## Impact:

Misinformation of events for critical parameters

## Tools Used

Manual Review

## Proof Of Concept:

```javascript
       function solelyDeposit(
        uint256 _nftId,
        address _poolToken,
        uint256 _amount
    ) public syncPool(_poolToken) {
        _handleSolelyDeposit(msg.sender, _nftId, _poolToken, _amount);

        _emitFundsSolelyDeposited(msg.sender, _nftId, _poolToken, _amount);

        _safeTransferFrom(_poolToken, msg.sender, address(this), _amount);
    }

```

## Recommended Mitigation Steps:

Function should emit event after safe transfer

```diff
    function solelyDeposit(
        uint256 _nftId,
        address _poolToken,
        uint256 _amount
    ) public syncPool(_poolToken) {
        _handleSolelyDeposit(msg.sender, _nftId, _poolToken, _amount);

-     _emitFundsSolelyDeposited(msg.sender, _nftId, _poolToken, _amount);
-     _safeTransferFrom(_poolToken, msg.sender, address(this), _amount);

+     _safeTransferFrom(_poolToken, msg.sender, address(this), _amount);
+     _emitFundsSolelyDeposited(msg.sender, _nftId, _poolToken, _amount);
    }

```


## Assessed type

Context
Unauthorized Assignment of Reserved NFTs to 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/PendlePowerFarm/PendlePowerManager.sol#L96-L140
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarm/PendlePowerManager.sol#L193
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseLending.sol#L1386-L1397
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L111-L127


# Vulnerability details

## Impact
The unauthorized assignment of reserved NFTs to users by an attacker with the reserve role could lead to a DoS for legitimate users, as they would be unable to use the reserved NFTs for their own positions. Additionally, the presence of dust in the NFTs could lead to unexpected calculation errors in other parts of the system that interact with these NFTs, potentially affecting the overall functionality and security of the system.

The [enterFarm](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarm/PendlePowerManager.sol#L96-L140) function in the `PendlePowerManager.sol` contract calls [_getWiseLendingNFT](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PowerFarms/PendlePowerFarm/PendlePowerManager.sol#L193), which in turn calls `_registrationFarm` which in turn calls [setRegistrationIsolationPool](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseLending.sol#L1386-L1397). This function checks if there is any collateral on the NFT by calling `_validateZero(WISE_SECURITY.overallETHCollateralsBare(_nftId))`. If there is any collateral, it reverts the transaction. Since the attacker has deposited dust into the NFTs, the `enterFarm` function will revert for any new user who tries to use these NFTs. This would prevent new users from participating in the farm.

## Proof of Concept
The [reservePositionForUser](https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/PositionNFTs.sol#L111-L127) function in the `PositionNFTs.sol` contract is marked with the `onlyReserveRole` modifier, which means that only addresses with the reserve role can call this function. However, this does not prevent anyone from calling `reservePositionForUser` if they have the reserve role. This could potentially allow an attacker with the reserve role to assign a reserved NFT to any user, which could be exploited to manipulate the system.

Here is the relevant code snippet from the `PositionNFTs.sol` contract:

```solidity
function reservePositionForUser(
    address _user
)
    onlyReserveRole
    external
    returns (uint256)
{
    return _reservePositionForUser(
        _user
    );
}
```
[M-3] farmContract address can become unchangeable
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#L51-L60


# Vulnerability details


## Description:
If `setFarmContract::farmContract` address is mistakenly set to a different address, it becomes unchangeable since we require to pass a check of `farmContract == ZERO_ADDRESS` before we can change the address which in this is going to fail.

## Impact:
we cant change farmContract address if it is not the zero address and if there is a need to change, the protocol will hav to redeployt the contract

## Tools Used
Manual Review

## Proof Of Concept:

```javascript
    function setFarmContract(
        address _farmContract
    )
        external
        onlyMaster
    {
        if (farmContract == ZERO_ADDRESS) {
            farmContract = _farmContract;
        }
    }
```

## Recommended Mitigation Steps:
Change the `setFarmContract` logic


## Assessed type

call/delegatecall
[M-2] Users can withdraw shares before required checks revert
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/PendlePowerManager.sol#L274-L302


# Vulnerability details

## Description:
Not following CEI. users an withdraw shares before the following check revert `_checkDebtRatio(wiseLendingNFT) == false` in `PendlePowerManager::manuallyWithdrawShares`

## Impact:
This could lead to loss of funds since the user can withdraw funds before the the collateral debt ratio get checked. Users with bad debts can withdraw their funds wich is against the protocol logic.

## Tools Used
Manual Review

## Proof Of Concept:

```javascript
    function manuallyWithdrawShares(
        uint256 _keyId,
        uint256 _withdrawShares
    )
        external
        updatePools
        onlyKeyOwner(_keyId)
    {
        uint256 wiseLendingNFT = farmingKeys[
            _keyId
        ];

        _manuallyWithdrawShares(
            wiseLendingNFT,
            _withdrawShares
        );

        if (_checkDebtRatio(wiseLendingNFT) == false) {
            revert DebtRatioTooHigh();
        }

        emit ManualWithdrawShares(
            _keyId,
            wiseLendingNFT,
            _withdrawShares,
            block.timestamp
        );
    }
```

## Recommended Mitigation Steps:
Check the debt ratio before allowing manual withdrawals

```diff
    function manuallyWithdrawShares(
        uint256 _keyId,
        uint256 _withdrawShares
    )
        external
        updatePools
        onlyKeyOwner(_keyId)
    {
        uint256 wiseLendingNFT = farmingKeys[
            _keyId
        ];

-        _manuallyWithdrawShares(
-            wiseLendingNFT,
-            _withdrawShares
-        );

-        if (_checkDebtRatio(wiseLendingNFT) == false) {
-            revert DebtRatioTooHigh();
-        }

+        if (_checkDebtRatio(wiseLendingNFT) == false) {
+            revert DebtRatioTooHigh();
+        }

+        _manuallyWithdrawShares(
+            wiseLendingNFT,
+            _withdrawShares
+        );


        emit ManualWithdrawShares(
            _keyId,
            wiseLendingNFT,
            _withdrawShares,
            block.timestamp
        );
    }

```


## Assessed type

ETH-Transfer
[M-1] Malicious user can dynamically delete availableNFT
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/PendlePowerManager.sol#L96-L140


# Vulnerability details

## Description:
When a user enters the farm with 0 eth, the `getWiseLendingNFT()`decreases and reverts at `_openPostion()` with - "AmountTooSmall()". but the `availableNFT` will be reduced already

## Impact:
Malicious deletion in `availableNFT`

## Tools Used
Manual Review

## Proof Of Concept:
```javascript
    function enterFarm(
        bool _isAave,
        uint256 _amount,
        uint256 _leverage,
        uint256 _allowedSpread
    )
        external
        isActive
        updatePools
        returns (uint256)
    {
        uint256 wiseLendingNFT = _getWiseLendingNFT();

        _safeTransferFrom(
            WETH_ADDRESS,
            msg.sender,
            address(this),
            _amount
        );

        _openPosition(
            _isAave,
            wiseLendingNFT,
            _amount,
            _leverage,
            _allowedSpread
        );

        uint256 keyId = _reserveKey(
            msg.sender,
            wiseLendingNFT
        );

        isAave[keyId] = _isAave;

        emit FarmEntry(
            keyId,
            wiseLendingNFT,
            _leverage,
            _amount,
            block.timestamp
        );

        return keyId;
    }

```

```javascript
     function _getWiseLendingNFT()
        internal
        returns (uint256)
    {
        if (availableNFTCount == 0) {

            uint256 nftId = POSITION_NFT.mintPosition();

            _registrationFarm(
                nftId
            );

            POSITION_NFT.approve(
                AAVE_HUB_ADDRESS,
                nftId
            );

            return nftId;
        }

        // @audit - this reduces the list of available NFTs
        return availableNFTs[
            availableNFTCount--
        ];
    }
```

## Recommended Mitigation Steps:
Make a check to ensure `_amount` is greater than zero
```diff
+   error AmountLessThanZero()

    function enterFarm(
        bool _isAave,
        uint256 _amount,
        uint256 _leverage,
        uint256 _allowedSpread
    )
        external
        isActive
        updatePools
        returns (uint256)
    {

+       if ( _amount < 0) {
+           revert AmountLessThanZero()
+       }

        uint256 wiseLendingNFT = _getWiseLendingNFT();

        _safeTransferFrom(
            WETH_ADDRESS,
            msg.sender,
            address(this),
            _amount
        );

        _openPosition(
            _isAave,
            wiseLendingNFT,
            _amount,
            _leverage,
            _allowedSpread
        );

        uint256 keyId = _reserveKey(
            msg.sender,
            wiseLendingNFT
        );

        isAave[keyId] = _isAave;

        emit FarmEntry(
            keyId,
            wiseLendingNFT,
            _leverage,
            _amount,
            b