xanoutas/solidity-security-findings huggingface dataset:
`PendleConnector._getPositionTVL()` will revert if called in the same block after a `PendleMarketV3._writeState()` operation. 
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/PendleConnector.sol#L271
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/PendleConnector.sol#L275
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/external/libraries/Pendle/PendlePtOracleLib.sol#L32
https://github.com/pendle-finance/pendle-core-v2-public/blob/e48767be54fd668c4ea693ac56d13186f2517e3d/contracts/core/Market/v3/PendleMarketV3.sol#L295
https://github.com/pendle-finance/pendle-core-v2-public/blob/e48767be54fd668c4ea693ac56d13186f2517e3d/contracts/core/Market/v3/PendleMarketV3.sol#L248
https://github.com/pendle-finance/pendle-core-v2-public/blob/e48767be54fd668c4ea693ac56d13186f2517e3d/contracts/core/Market/OracleLib.sol#L136


# Vulnerability details

## Impact  
Function `PendleConnector._getPositionTVL()` will revert unexpectedly, subsequently causing functions `TVLHelper.getTVL()` and `AccountingManager.TVL()` to revert, which causes DOS for the users same-block transactions such as deposits, withdraws, recording profit, or adjusting the profit.  
## Proof of Concept  
`PendleConnector._getPositionTVL()` relies on `PendleLpOracleLib.getLpToAssetRate()` and `PendlePtOracleLib.getPtToAssetRate()`. Downstream, the functions rely on `PendlePtOracleLib._getMarketLnImpliedRate()` which makes a call to `PendleMarketV3.observe()` with a duration hardcoded to 10 [code snippet 1](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/PendleConnector.sol#L271-L275),[code snippet 2](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/external/libraries/Pendle/PendlePtOracleLib.sol#L32). 

The observations in `PendleMarketV3` are updated every time there is a `PendleMarketV3._writeState()` call, for example, on a `PendleMarketV3.mint()` call, `PendleMarketV3.burn()` call, `PendleMarketV3.swapExactPtForSy()` call, or a `PendleMarketV3.swapSyForExactPt()` call. 

The issue is that if in the same block, after the `PendleMarketV3._writeState()` operation, there is a call to `PendleMarketV3.observe()` it will revert with `OracleTargetTooOld` - [reverting line snippet](https://github.com/pendle-finance/pendle-core-v2-public/blob/e48767be54fd668c4ea693ac56d13186f2517e3d/contracts/core/Market/OracleLib.sol#L136). Such a revert will then cause `TVLHelper.getTVL()` to revert and subsequently `TVL()` to revert when it is called to process deposits, withdrawals, or fee requests in `AccountingManager`. 

The `PendleMarketV3.observe()` revert will happen in blocks where any previous transaction (within the block), regardless if originating from a Vault Connector or an outside user of the Pendle Market, updates the observations in the Market through a `mint()`, `burn()`, `swapExactPtForSy()` or `swapSyForExactPt()` transactio
When the startBridgeTransaction is triggered, it doesn't have the 'payable'.
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/OmniChainHandler/OmnichainLogic.sol#L68-L84


# Vulnerability details

---

# **Vulnerability Detail**

According to the docs, executeBridge is used to bridge tokens to another chain and it’s being used by “Omnichain logic”. However the starting flow begins in the startBridgeTransaction, which doesn’t have the payable. It means that the function expect to revert.

`startBridgeTransaction` initiates a bridge transaction if it has been approved and the waiting time has passed. However, it doesn’t have ‘payable’. 

When the `startBridgeTransaction` is called by the manager, it calls the `executeBridge` in the swapHandler contract, which has the ‘payable’. If we examine the further flow we will end up in the Lifi contract on the `performBridgeAction` which _forward and eventually make a call.

```solidity
lifi.call{ value: msg.value }(data);
```

But, everything starts with the `startBridgeTransaction` function, which has no ‘payable’, thus it sends no msg.value. So, the function will always revert.

# **Proof of Concept**

1. Manager calls the `startBridgeTransaction` function
2. It triggers `swapHandler.executeBridge` which is payable
3. It calls LifiImplementation `performBridgeAction` which is payable and this function performs a call `lifi.call{ value: msg.value }(data);` . But no ETH was provided, and function would revert

# **Impact**

The bridge transaction couldn’t be executed. 

**#Tools Used**

Manual review

# **Recommendation**

Add ‘payable’ and make sure that the respective msg.value is provided


## Assessed type

Error
When `SiloConnector` borrows token from Silo, it doesn't consider healthfactor correctly
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/SiloConnector.sol#L85-L90


# Vulnerability details

## Impact
Noya interacts Silo with `SiloConnector`. When a keeper contract borrows token from Silo, health factor is not considered properly. This cause the risk to the management of management system.

## Proof of Concept

Noya interacts Silo with `SiloConnector`. To borrow tokens from Silo, a keeper calls the function [borrow()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/SiloConnector.sol#L85-L90). 

Silo only checks if borrowed amount is less than collateral while borrowing. However, Noya sets the variable `minimumHealthFactor` additionally for `SiloConnector` to manage risk of the position.
This idea is implemented for the function [withdraw()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/SiloConnector.sol#L52-L69) from L65.


```solidity
File: contracts\connectors\SiloConnector.sol
52:     function withdraw(address siloToken, address wToken, uint256 amount, bool oC, bool closePosition)
53:         external
54:         onlyManager
55:         nonReentrant
56:     {
57:         ISilo silo = ISilo(siloRepository.getSilo(siloToken));
58:         silo.withdraw(wToken, amount, oC);
59:         _updateTokenInRegistry(wToken);
60:         if (closePosition && isSiloEmpty(silo)) {
61:             registry.updateHoldingPosition(
62:                 vaultId, registry.calculatePositionId(address(this), SILO_LP_ID, abi.encode(siloToken)), "", "", true
63:             );
64:         }
65:         if (!SolvencyV2.isSolvent(silo, address(this), minimumHealthFactor)) {  //@audit-info check health factor
66:             revert IConnector_LowHealthFactor(0);
67:         }
68:         emit Withdraw(siloToken, wToken, amount, oC, closePosition);
69:     }
```
However, there is no check if health factor of the position is bigger than `minimumHealthFactor` in the function [borrow()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/SiloConnector.sol#L85-L90) at L87 when a keeper borrows token from Silo.

```solidity
File: contracts\connectors\SiloConnector.sol
85:     function borrow(address siloToken, address bToken, uint256 amount) external onlyManager nonReentrant {
86:         ISilo silo = ISilo(siloRepository.getSilo(siloToken));
87:         silo.borrow(bToken, amount);
88:         _updateTokenInRegistry(bToken);
89:         emit Borrow(siloToken, bToken, amount);
90:     }
```

As a result, the risk of the position is not managed properly, and it causes risk to the Vault.

## Tools Used
Manual Review

## Recommended Mitigation Steps

```diff
File: contracts\connectors\SiloConnector.sol
85:     function borrow(address siloToken, address bToken, uint256 amount) external onlyManager nonReentrant
Loss of funds in `PendleConnector.depositIntoMarket()`
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/PendleConnector.sol#L112-L119
https://github.com/pendle-finance/pendle-core-v2-public/blob/e48767be54fd668c4ea693ac56d13186f2517e3d/contracts/core/Market/v3/PendleMarketV3.sol#L86-L97
https://github.com/pendle-finance/pendle-core-v2-public/blob/e48767be54fd668c4ea693ac56d13186f2517e3d/contracts/core/Market/v3/PendleMarketV3.sol#L223-L229


# Vulnerability details

## Impact  
`PendleConnector` contracts can lose out on funds when minting LP tokens.  
## Proof of Concept 
The `PendleMarketV3.mint()` function accepts as parameters `uint256 netSyDesire` and `uint256 netPtDesired` , which serve as upper bounds, where the Market will try and mint as many LP tokens as possible such that neither of the upper bound amounts are exceeded. The issue is that in `PendleConnector.depositIntoMarket`, the developers incorrectly assume a call to `PendleMarketV3.skim()` will return back any unused tokens that were sent. The `skim()` function sends the extra tokens to the Pendle treasury rather than the calling contract: [skim()](https://github.com/pendle-finance/pendle-core-v2-public/blob/e48767be54fd668c4ea693ac56d13186f2517e3d/contracts/core/Market/v3/PendleMarketV3.sol#L223-L229).
```solidity
    * @dev skim allows us to get the surplus tokens
    */
function depositIntoMarket(IPMarket market, uint256 SYamount, uint256 PTamount) external onlyManager nonReentrant {
    (IPStandardizedYield _SY, IPPrincipalToken _PT,) = IPMarket(market).readTokens();
    IERC20(address(_SY)).safeTransfer(address(market), SYamount);
    IERC20(address(_PT)).safeTransfer(address(market), PTamount);
    market.mint(address(this), SYamount, PTamount);
    market.skim();
    emit DepositIntoMarket(address(market), SYamount, PTamount);
}
```

## Tools used
Manual Inspection
## Recommended Mitigation Steps
Remove the call to `market.skim()` and integrate with `MarketMathCore.addLiquidity()` to pre-calculate the used `SY` and `PT` amounts which then can be supplied to `market.mint()`. This will ensure no tokens are lost.  


## Assessed type

Context
Bridge actions can't be done when it is required to pay fee with the native token of the chain
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/SwapHandler/GenericSwapAndBridgeHandler.sol#L126-L140


# Vulnerability details

## Impact
The function `executeBridge()` perform bridge action via Lifi. If a user should pay fee to `Lifi` as the native token of the chain, the function is reverted and bridge action can't be done. As a result, a manager can't do any bridge action via `Lifi`. It affects crosschain property of the protocol.

## Proof of Concept

The bridge action is done by calling the function [executeBridge()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/SwapHandler/GenericSwapAndBridgeHandler.sol#L126-L140). An eligible user can call the function with the native token like `ether`. The contract calls the function `route.performBridgeAction()` with `msg.value = 0` from L137.
```solidity
File: contracts\helpers\SwapHandler\GenericSwapAndBridgeHandler.sol
126:     function executeBridge(BridgeRequest calldata _bridgeRequest)
127:         external
128:         payable
129:         onlyEligibleUser
130:         onlyExistingRoute(_bridgeRequest.routeId)
131:         nonReentrant
132:     {
133:         RouteData memory bridgeImplInfo = routes[_bridgeRequest.routeId];
134: 
135:         if (!bridgeImplInfo.isBridge) revert RouteNotAllowedForThisAction(); 
136: 
137:         ISwapAndBridgeImplementation(bridgeImplInfo.route).performBridgeAction(msg.sender, _bridgeRequest);
138: 
139:         emit BridgeExecutionCompleted(_bridgeRequest);
140:     }
```
Then the function [performBridgeAction()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/SwapHandler/Implementaions/LifiImplementation.sol#L133-L142) calls the function [_forward()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/SwapHandler/Implementaions/LifiImplementation.sol#L165-L183) and in that function, it calls `lifi` with `msg.value` and `msg.data` as payload from L176. As `msg.value` is `0`, no native token is transfered to `lifi`.
```solidity
File: contracts\helpers\SwapHandler\Implementaions\LifiImplementation.sol
133:     function performBridgeAction(address caller, BridgeRequest calldata _request)
134:         external
135:         payable
136:         override
137:         onlyHandler
138:     {
139:         verifyBridgeData(_request);
140:         _forward(IERC20(_request.inputToken), _request.from, _request.amount, caller, _request.data, _request.routeId);
141:         emit Bridged(_request.from, _request.inputToken, _request.amount, _request.data);
142:     }


165:     function _forward(IERC20 token, address from, uint256 amount, address caller, bytes calldata data, uint256 routeId)
166:         internal
167:         virtual
168:         nonReentrant
169:     {
             [...]
176:         (bool success, bytes memory
Incomplete TVL Calculation in `AerodromeConnector::_getPositionTVL` Function.
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/AerodromeConnector.sol#L125-L134


# Vulnerability details

## Impact

The [AerodromeConnector::stake](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/AerodromeConnector.sol#L100) function allows to stake liquidity tokens to the gauge pool. However, the [AerodromeConnector::_getPositionTVL](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/AerodromeConnector.sol#L125) function, which is responsible for calculating the Total Value Locked (TVL), does not take into account the liquidity tokens deposited in the gauge pool. This oversight will result in an incorrect calculation of the TVL, which could negatively impact the overall functionality of the protocol that utilizes the TVL metric.

## Proof of Concept

The [AerodromeConnector::stake](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/AerodromeConnector.sol#L100) function allows to deposit and stake liquidity tokens in the gauge pool.

```solidity
AerodromeConnector.sol
100: function stake(address pool, uint256 liquidity) public onlyManager nonReentrant {
101:         address gauge = voter.gauges(pool);
102:         IERC20(pool).forceApprove(address(gauge), liquidity);
103:         IGauge(gauge).deposit(liquidity, address(this));  // @audit-info this sends tokens to the gauge
104:     }
```

The implementation of the [Gauge::deposit](https://github.com/aerodrome-finance/contracts/blob/b934e7ae398ea6c251a4d5af2119776c06f1f23d/contracts/gauges/Gauge.sol#L151) is as follows:

```solidity
    function deposit(uint256 _amount, address _recipient) external {
        _depositFor(_amount, _recipient);
    }

    function _depositFor(uint256 _amount, address _recipient) internal nonReentrant {
        if (_amount == 0) revert ZeroAmount();
        if (!IVoter(voter).isAlive(address(this))) revert NotAlive();


        address sender = _msgSender();
        _updateRewards(_recipient);


        IERC20(stakingToken).safeTransferFrom(sender, address(this), _amount); // @audit-info liquidity tokens are sent to the gauge.
        totalSupply += _amount;
        balanceOf[_recipient] += _amount;


        emit Deposit(sender, _recipient, _amount);
    }
```

As evident from the preceding code snippet, when the `Gauge::deposit` function is invoked, staked liquidity tokens are sent to the gauge pool. However, the implementation of the [AerodromeConnector::_getPositionTVL](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/AerodromeConnector.sol#L125) function only accounts for the liquidity tokens held in the Aerodrome pool, and does not consider the tokens that have been staked in the gauge pool. This oversight in the [AerodromeConnector::_getPositi
The function `MorphoBlueConnector::_getPositionTVL()` does not calculate `tvl` of position correctly
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/MorphoBlueConnector.sol#L118-L135


# Vulnerability details

## Impact
Noya interacts MorphoBlue with `MorphoBlueConnector`. To calculate the value of position on MorphoBlue, it uses the function `_getPositionTVL()`. However, it does not calculates the value of position correctly. As a result, `TVL` of the vaults that hold position on MorphoBlue is calculated incorrectly, it affects overall functionality of the protocol.

## Proof of Concept

Noya interacts with MorphoBlue `MorphoBlueConnector`. To calculate the value of position on MorphoBlue, it uses the function `_getPositionTVL()`. 

The `tvl` of position is calculated with `supplyAmount`, the amount of `loanToken` supplied to MorphoBlue, `borrowAmount`, the amount of `loanToken` borrowed from MorphoBlue and the amount of collateral in terms of `loanToken` from L132.

However, as the `borrowAmount` represents the amount of `loanToken` borrowed from MorphoBlue, it should be subtracted. 

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/MorphoBlueConnector.sol#L118-L135

```solidity
File: contracts\connectors\MorphoBlueConnector.sol
118:     function _getPositionTVL(HoldingPI memory p, address base) public view override returns (uint256 tvl) {
119:         PositionBP memory positionInfo = registry.getPositionBP(vaultId, p.positionId);
120:         if (positionInfo.positionTypeId == MORPHO_POSITION_ID) {
121:             Id id = abi.decode(positionInfo.data, (Id));
122:             MarketParams memory params = morphoBlue.idToMarketParams(id);
123:             Market memory market = morphoBlue.market(id);
124:             Position memory pos = morphoBlue.position(id, address(this));
125:             uint256 borrowAmount =
126:                 uint256(pos.borrowShares).toAssetsUp(market.totalBorrowAssets, market.totalBorrowShares);
127:             uint256 supplyAmount =
128:                 uint256(pos.supplyShares).toAssetsUp(market.totalSupplyAssets, market.totalSupplyShares);
129:             tvl = _getValue(
130:                 params.loanToken,
131:                 base,
132:                 supplyAmount + borrowAmount + convertCToL(pos.collateral, params.oracle, params.collateralToken)  // @audit-info borrowAmount should be subtracted
133:             );
134:         }
135:     }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps

```diff
File: contracts\connectors\MorphoBlueConnector.sol
118:     function _getPositionTVL(HoldingPI memory p, address base) public view override returns (uint256 tvl) {
119:         PositionBP memory positionInfo = registry.getPositionBP(vaultId, p.positionId);
120:         if (positionInfo.positionTypeId == MORPHO_POSITION_ID) {
121:             Id id = abi.decode(positionInfo.data, (Id));
122:             MarketParams memory params = morphoBlue.idToMarketPara
Inaccurate Total Value Locked Calculation Due to Improper Token Decimal Handling
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/OmniChainHandler/OmnichainManagerNormalChain.sol#L19-L31


# Vulnerability details

## Impact

The Noya vault calculates its `Total Value Locked (TVL)` based on the current positions during withdrawal or deposit requests. To aggregate the total value of positions across multiple chains, the vault passes this information through `LayerZero` to the base chain. However, the protocol does not properly handle the token decimal places in this TVL calculation process.

Specifically, for vaults with `USDC` or `USDT` as the base token, the value of positions on the `BNB` chain is calculated using 18 decimal places, while the value of positions on the base chain is calculated using 6 decimal places. As a result, the `TVL` of the vault on the base chain is not accurately calculated if the vault has positions on other chains. This leads to an overestimation of the `TVL`, which can significantly impact the overall functionality and reporting of the protocol.

## Proof of Concept

The Noya protocol utilizes two key contracts, `OmnichainManagerBaseChain` and `OmnichainManagerNormalChain`, to manage its omnichain position handling. Within this architecture, the sender contracts are deployed on chains other than the base chain and are responsible for communicating the `Total Value Locked (TVL)` information to the base chain.

Specifically, the `OmnichainManagerNormalChain` contract deployed on the BNB chain for vaults with `USDC` as the base token calculates the `TVL` of positions on the BNB chain in terms of `USDC`. However, on the `BNB` chain, the `USDC` token has 18 decimal places, so the `TVL` is calculated using this 18-decimal format.

This discrepancy in decimal place handling between the sender contract on the `BNB` chain and the receiver contract on the base chain can lead to inaccuracies in the overall `TVL` calculation for the vault. The `TVL` value reported on the base chain may be overestimated due to the inconsistent decimal place usage, which can have significant implications for the protocol's functionality and reporting.

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/OmniChainHandler/OmnichainManagerNormalChain.sol#L19-L31

```solidity
File: helpers\OmniChainHandler\OmnichainManagerNormalChain.sol
19:     function getTVL() public view returns (uint256) {
20:         (, address baseToken) = registry.getVaultAddresses(vaultId);
21:         return TVLHelper.getTVL(vaultId, registry, baseToken); //@audit-info calculates tvl of positions on BN chain in terms of USDC whose decimal is 18
22:     }

28:     function updateTVLInfo() external onlyManager {
29:         uint256 tvl = getTVL();
30:         LZHelperSender(lzHelper).updateTVL(vaultId, tvl, block.timestamp); //@audit-info send information of tvl to Base chain
31:     }
```

The `OmnichainManagerNormalChain` contra