xanoutas/solidity-security-findings huggingface dataset:
Token Blacklist could DOS withdraw
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L439


# Vulnerability details

## Impact
A malicious user can DOS execution of withdrawal
```solidity
if (withdrawFeeAmount > 0) {
            baseToken.safeTransfer(withdrawFeeReceiver, withdrawFeeAmount);
        }
```

## Proof of Concept
1. Alice deposit small amount and set withdrawal to an account that has been blacklisted by used in a used market.
2. When withdrawal is processed in batch by an admin, execution is denied since transfer of USDC to a blacklisted address.
```solidity
  // loop through the withdraw queue and execute the withdraws
        while (
            currentWithdrawGroup.lastId > firstTemp
                && withdrawQueue.queue[firstTemp].calculationTime + withdrawWaitingTime <= block.timestamp
                && i < maxIterations
        ) {
            i += 1;
            WithdrawRequest memory data = withdrawQueue.queue[firstTemp];
            uint256 shares = data.shares;
            // calculate the base token amount that the user will receive based on the total available amount
            uint256 baseTokenAmount =
                data.amount * currentWithdrawGroup.totalABAmount / currentWithdrawGroup.totalCBAmountFullfilled;

            withdrawRequestsByAddress[data.owner] -= shares;
            _burn(data.owner, shares);

            processedBaseTokenAmount += data.amount;
            {
                uint256 feeAmount = baseTokenAmount * withdrawFee / FEE_PRECISION;
                withdrawFeeAmount += feeAmount;
                baseTokenAmount = baseTokenAmount - feeAmount;
            }

            baseToken.safeTransfer(data.receiver, baseTokenAmount);
            emit ExecuteWithdraw(
                firstTemp, data.owner, data.receiver, shares, data.amount, baseTokenAmount, block.timestamp
            );
            delete withdrawQueue.queue[firstTemp];
            // increment the first index of the withdraw queue
            firstTemp += 1;
        }
```


## Tools Used
Manual review

## Recommended Mitigation Steps
Allow users to withdraw funds after computation has been completed


## Assessed type

Context
zero amount transfers can fail.
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/PendleConnector.sol#L99
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/PendleConnector.sol#L114
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/PendleConnector.sol#L115
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/PendleConnector.sol#L189
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/PendleConnector.sol#L204
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/PendleConnector.sol#L221
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/BalancerFlashLoan.sol#L76
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/BalancerFlashLoan.sol#L91


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
Some ERC20 tokens do not allow zero value transfers, reverting such attempts.

Some ERC20 tokens revert on zero value transfers:

https://github.com/d-xo/weird-erc20#revert-on-zero-value-transfers
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

            tokens[i].safeTransfer(msg.sender, amounts[i]);

## Tools Used

## Recommended Mitigation Steps
check for amount greater than 0 before safetransfer.


## Assessed type

ERC20
Use of `slot0` to get `sqrtPriceX96` can lead to price manipulation.
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/UNIv3Connector.sol#L140


# Vulnerability details

## Impact
Pool sqrtPriceX96 value can be manipulated.

## Proof of Concept
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/UNIv3Connector.sol#L140
```
(uint160 sqrtPriceX96,,,,,,) = pool.slot0();
```

## Tools Used
Code review

## Recommended Mitigation Steps
Use the `TWAP` function to get the value of `sqrtPriceX96`.


## Assessed type

MEV
Incorrect recipient address in sendTokensToTrustedAddress functions
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/accountingManager/AccountingManager.sol#L150-L160


# Vulnerability details

## Impact
The function `sendTokensToTrustedAddress` incorrectly transfers tokens to `msg.sender` instead of the `_caller` address. Dev also used `_caller` parameter but `_caller` value  pass only in emit. This issue is critical, especially if the token being transferred is a blacklisted token like `USDT` and `msg.sender` is blacklisted. This could lead to loss of funds or unintended recipients receiving the tokens.

```solidity
function sendTokensToTrustedAddress(address token, uint256 amount, address _caller, bytes calldata _data)
```


## Proof of Concept
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/accountingManager/AccountingManager.sol#L150-L160
```solidity
function sendTokensToTrustedAddress(address token, uint256 amount, address _caller, bytes calldata _data)
        external
        returns (uint256)
    {
        emit TransferTokensToTrustedAddress(token, amount, _caller, _data);
        if (registry.isAnActiveConnector(vaultId, msg.sender)) {
            IERC20(token).safeTransfer(address(msg.sender), amount); //@audit must be _caller?
            return amount;
        }
        return 0;
    }
```

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/helpers/ConnectorMock2.sol#L27-L38


## Tools Used
Manual code analysis

## Recommended Mitigation Steps
For AccountingManager.sol
change this  `IERC20(token).safeTransfer(address(msg.sender), amount);`
to `IERC20(token).safeTransfer(_caller), amount);

For ConnectorMock2.sol
change this  `IERC20(token).safeTransfer(address(msg.sender), amount);`
to `IERC20(token).safeTransfer(caller), amount);` 


## Assessed type

Token-Transfer
Base tokens like USDT, USDC having different decimals on different chains can have their TVL updated incorrectly
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Tokens like [USDT, USDC](https://bscscan.com/address/0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d#readProxyContract#F3) on the BSC chain have 18 decimals while the "Base" blockhain which is being used as the root/base chain uses 6 decimals (see [here](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913#readProxyContract)).

The issue is that when the TVL is updated on the base chain by sending a cross-chain message call from OmnichainManagerNormalChain.sol contract to OmnichainManagerBaseChain.sol contract, the TVL is recorded in the holding position with 18 decimals instead of scaling it down to 6 decimals. 

Due to this, whenever the accounting manager retrieves the [TVL()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L627C1-L630C6) through function [totalAssets()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L592) (overriden from ERC4626), the TVLHelper contract would loop through all holding positions and sum them up in order to be used in vault operations like minting shares etc. This would be problematic since the tvl is 1e12 decimals more than the actual of 1e6. This breaks the accounting in the accounting manager contract.

## Proof of Concept
Here's the whole process:

1. Manager calls function [updateTVLInfo()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/OmniChainHandler/OmnichainManagerNormalChain.sol#L28) on the OmnichainManagerNormalChain.sol contract on the BSC chain.
 - On Line 29, it calls the getTVL() function that loops through all the holding positions for the base token and vaultId. 
 - The value is returned (in 18 decimals since we're on BSC) and stored in `tvl` on Line 29.
  Line 30 calls the updateTVL() function on the lzhelper contract, which sends the cross-chain message with the tvl update.
```solidity
File: OmnichainManagerNormalChain.sol
19:     function getTVL() public view returns (uint256) {
20:         (, address baseToken) = registry.getVaultAddresses(vaultId);
21:         return TVLHelper.getTVL(vaultId, registry, baseToken);
22:     }
23:     /**
24:      * Triggers an update of the vault's TVL information, sending the latest data to the base chain via the LZHelperSender contract.
25:      * This function is restricted to be called by managers only, ensuring that TVL updates are controlled and authorized.
26:      */
27: 
28:     function updateTVLInfo() external onlyManager {
29:         uint256 tvl = getTVL();
30:         LZHelperSender(lzHelper).updateTVL(vaultId, tvl, block.timestamp);
31:     }
```

2. The call arrives on the base chain
`NoyaValueOracle.getValue` returns an incorrect price when a multi-token route is used
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L89


# Vulnerability details

The [NoyaValueOracle.getValue](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L71-L79) is used to convert the price of an `asset` to `baseToken`, using token routing when necessary.

If there is no oracle for two tokens, a route needs to be set up between them. This route can then be used to get a value using [NoyaValueOracle.getValue](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L71-L79).

However, there's a mistake in the [NoyaValueOracle.getValue](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L71-L79) function that makes it give the wrong price.

## Impact

The [NoyaValueOracle.getValue](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L71-L79) will return incorrect prices for tokens when the configured [priceRoutes[asset][baseToken].length](https://github.com/code-423n4/2024-04-noya/blob/main/contracts/helpers/valueOracle/NoyaValueOracle.sol#L76C36-L76C65) is more than 1.

## Proof of Concept

To set a price path for a token pair, the maintainer should use [NoyaValueOracle.updatePriceRoute](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L61-L64).

For instance, for the `SOL` to `UNI` conversion. 1 `SOL` is roughly equal to 21 `UNI`, but there's no direct oracle. So, the maintainer sets a route `SOL` → `BNB` → `ETH` → `UNI`.

When [\_getValue](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L81-L93) tries to convert 100 `SOL` into `UNI`, it should first convert `SOL` → `BNB`, then `BNB` → `ETH`, and lastly `ETH` → `UNI`. But, what [\_getValue](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L81-L93) really does is to convert `SOL` → `BNB`, `SOL` → `ETH`, and `SOL` → `UNI`.

You can see this error in the [\_getValue](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/valueOracle/NoyaValueOracle.sol#L89) function, where `asset` is used each time instead of `quotingToken`.

```diff
    function _getValue(address asset, address baseToken, uint256 amount, address[] memory sources)
        internal
        view
        returns (uint256 value)
    {
        uint256 initialValue = amount;
        address quotingToken = asset;
        for (uint256 i = 0; i < sources.length; i++) {
>
`executeWithdraw` may be blocked if any of the users are blacklisted from the `baseToken`
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L428


# Vulnerability details

https://github.com/d-xo/weird-erc20#tokens-with-blocklists

> Some tokens (e.g. `USDC`, `USDT`) have a contract level admin controlled address blocklist. If an address is blocked, then transfers to and from that address are forbidden.

> Malicious or compromised token owners can trap funds in a contract by adding the contract address to the blocklist. This could potentially be the result of regulatory action against the contract itself, against a single user of the contract (e.g. a Uniswap LP), or could also be a part of an extortion attempt against users of the blocked contract.

If a user whose address has been blocklisted is added to a `withdrawQueue` inside the `AccountingManager` contract, all other users that are in that same queue will not be able to withdraw, as the `executeWithdraw` function will revert when it tries to do `[baseToken.safeTransfer](https://github.com/code-423n4/2024-04-noya/blob/main/contracts/accountingManager/AccountingManager.sol#L428)` call on the blocklisted address.

## Impact

A user who has been blocklisted can be added to a `withdrawQueue` to DoS the call of the `executeWithdraw` function, effectively preventing all other user that are in that same queue from withdrawing, locking their assets inside the protocol.

## Proof of Concept

When `baseToken` has blocklisting functionality, and any user in the withdrawal queue is in the blocklist, it prevents all other users from making withdrawals.

_Place the following test in [testFoundry/BlacklistableTokenPOC.t.sol](https://github.com/code-423n4/2024-04-noya/tree/9c79b332eff82011dcfa1e8fd51bad805159d758/testFoundry) and run it with the command `forge test --mt testBlacklistableERC20`_

```solidity
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;

import "@openzeppelin/contracts-5.0/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-5.0/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-5.0/token/ERC20/ERC20.sol";
import "./utils/testStarter.sol";
import "./utils/resources/OptimismAddresses.sol";
import { AaveConnector, BaseConnectorCP } from "contracts/connectors/AaveConnector.sol";

contract BlacklistERC20 is ERC20 {
    mapping(address => bool) private blacklisted;

    error AccountBlacklisted();

    constructor() ERC20("Blacklist ERC20", "BLT") {
        _mint(msg.sender, 100e18);
    }

    function addToBlacklist(address account) public  {
        blacklisted[account] = true;
    }

    function isBlacklisted(address account) public view returns (bool) {
        return blacklisted[account];
    }

    function _update(
        address from,
        address to,
        uint256 value
    ) internal override {
        if (isBlacklisted(from) || isBlacklisted(to)) revert AccountBlacklisted();
        super._update(from, to, value);
    }
}

con
BalancerConnector use `totalSupply()` to get balance of pool, which is not compatitive with most pools
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/BalancerConnector.sol#L167


# Vulnerability details

## Vulnerability details
Balancer pools have different methods to get total supply of minted LP tokens, which can be read at here: https://docs.balancer.fi/concepts/advanced/valuing-bpt/valuing-bpt.html#getting-bpt-supply.

Document said that `totalSupply` is only used for older pools, and should not be used without checking it first. And `getActualSupply` is used by most of new pools. For example:

https://etherscan.io/address/0x9f9d900462492d4c21e9523ca95a7cd86142f298
https://etherscan.io/address/0x3ff3a210e57cfe679d9ad1e9ba6453a716c56a2e
https://etherscan.io/address/0xcf7b51ce5755513d4be016b0e28d6edeffa1d52a

Since the newer pools have pre-minted BPT, which do not have in old pools. Because of that, the interaction with newer weighted pools and also the future weighted pools would not be accurate since the wrong total supply is used

## Impact
Wrong positionTVL is returned if protocol interact with new pool

## Tools Used
Manual review

## Recommended Mitigation Steps
Check first if the pool you that interacting with is a newer one and uses the `getActualSupply` or if it is an older one and uses `totalSupply`, in that way the protocol could interact with multiple pools in the long run.


## Assessed type

Other
totalAssigned and totalDeposited used incorrectly to calculate TVL
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

# **Vulnerability Detail**

The _getPositionTVL in the SNX connector returns incorrect TVL because it add totalAssigned to the totalDeposited which is already included in the totalDeposited. So it artificially increases the TVL. 

According to the docs and response from the Synthetix dev we can see that:

1. totalAssigned = The amount of collateral in the account that is delegated to pools.
2. totalDeposited is the full amount of collateral in a users account. of that, totalAssigned is the portion of the collateral that has been delegated to pools (in the future there may be other ways to "assign" collateral" so yes, using totalDeposited purely should be good

# **Proof of Concept**

```solidity
function _getPositionTVL(HoldingPI memory p, address base) public view override returns (uint256 tvl) {

        (uint128 accountId, address collateralType) = abi.decode(p.data, (uint128, address));
        
        
        (uint256 totalDeposited, uint256 totalAssigned, uint256 totalLocked) = SNXCoreProxy.getAccountCollateral(accountId, collateralType);
        tvl = _getValue(collateralType, base, totalDeposited + totalAssigned);
```

# **Impact**

The TVL artificially inflated

# **Mitigation step**

Don’t add totalAssigned.  Use totalDeposited purely.


## Assessed type

Error
Blacklisted user could prevent the executeWithdraw function from execution. And we can not start another withdraw group until the current withdraw group is fullfilled
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L396-L432


# Vulnerability details

# **Vulnerability Detail**

After the waiting period is passed, the executeWithdraw triggered by the manager to burn the user’s share and safeTransfer the token to them. However, if one of the users would make smth malicious and he would blacklisted by the USDC/USDT the safeTransfer to them would fail/revert. The point is that other user’s will suffer from it and will not be able to receive their funds because they are stored in the currentWithdrawGroup mapping and there is no way to exclude this “blacklisted” address from it. Additionally, the overall withdrawal group could not be executed
	

# **Proof of Concept**

At present, withdrawal requests are placed in an array and executed one after the other using a for loop. Nonetheless, if a safeTransfer() is attempted to a blacklisted user of USDC, the operation will be unsuccessful. This issue further complicates the withdrawal system as the blacklisted user remains unaddressed, potentially causing the system to malfunction.

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L406-L428

In this scenario the resetMiddle function will not be able to save the situation because the withdrawalGroup is already started.

```solidity
 if (newMiddle > withdrawQueue.middle || newMiddle < withdrawQueue.first || currentWithdrawGroup.isStarted) {
                revert NoyaAccounting_INVALID_AMOUNT();
            }
```

# **Impact**

The withdrawal mechanism will be malfunction and DoS

# **Recommendation**

I would recommend:

1. Create the opportunity to exclude some address from the mapping
2. Allow users to whom the transfer is failed withdraw their tokens manually from the contract when the waiting period passed.

# **References**

https://solodit.xyz/issues/m-16-usdc-blacklisted-accounts-can-dos-the-withdrawal-system-code4rena-hubble-hubble-contest-git


## Assessed type

DoS
When all available funds from a connector are deployed to the protocol, the holding position is removed, resulting in incorrect TVL
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/BaseConnector.sol#L159
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/BaseConnector.sol#L142-L145
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/Registry.sol#L359


# Vulnerability details

The `TVL` calculation represents the total assets managed in the vault. The formula is:

```solidity
    function TVL() public view returns (uint256) {
        return TVLHelper.getTVL(vaultId, registry, address(baseToken))
		        + baseToken.balanceOf(address(this))
            - depositQueue.totalAWFDeposit;
    }
```

- The `TVLHelper` calculates the total `TVL` of all `holdingPositions`. The `getPositionTVL` formula is specific to the protocol's connector.

```solidity
    function getTVL(uint256 vaultId, PositionRegistry registry, address baseToken) public view returns (uint256) {
        uint256 totalTVL;
        uint256 totalDebt;
        HoldingPI[] memory positions = registry.getHoldingPositions(vaultId);
        for (uint256 i = 0; i < positions.length; i++) {
            if (positions[i].calculatorConnector == address(0)) {
                continue;
            }
            uint256 tvl = IConnector(positions[i].calculatorConnector).getPositionTVL(positions[i], baseToken);
            bool isPositionDebt = registry.isPositionDebt(vaultId, positions[i].positionId);
            if (isPositionDebt) {
                totalDebt += tvl;
            } else {
                totalTVL += tvl;
            }
        }
        if (totalTVL < totalDebt) {
            return 0;
        }
        return (totalTVL - totalDebt);
    }
```

- Executed deposits in the `AccountingManager` (`balanceOf(AccountingManager)` - `totalAWFDeposit`)

Let's first look how a user can deposit funds and how these funds are deposited into a specific protocol to gain yield:

1. The user calls `AccountingManager.deposit()`, which transfers the tokens from the user to the `AccountingManager`. This action creates a deposit request and adds it to the deposit queue.
2. The strategy manager calls `AccountingManager.calculateDepositShares()` to calculate the shares that should be received by the `depositRequest` owners.
3. The strategy manager calls `AccountingManager.executeDeposit()`. During this step, two actions occur:
   1. The shares calculated in the previous step are minted to the users.
   2. The liquidity from the users' deposits is transferred to the specified connector.

The transfer of the liquidity is happening through [Connector.addLiquidity()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L293)

```solidity
    function executeDeposit(uint256 maxI, address connector, bytes memory addLPdata)
        public
        onlyManager