msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Malicious user can use an excessively large _toAddress in OFTCore#sendFrom to break layerZero communication
Severity: high

Description:
By default layerZero implements a blocking behavior, that is, that each message must be processed and succeed in the order that it was sent. In order to circumvent this behavior the receiver must implement their own try-catch pattern. If the try-catch pattern in the receiving app ever fails then it will revert to its blocking behavior. The _toAddress input to OFTCore#sendFrom is calldata of any arbitrary length. An attacker can abuse this and submit a send request with an excessively large _toAddress to break communication between network with different gas limits.\\n```\\nfunction sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) public payable virtual override {\\n    _send(_from, _dstChainId, _toAddress, _amount, _refundAddress, _zroPaymentAddress, _adapterParams);\\n}\\n```\\n\\nThe _toAddress input to OFTCore#sendFrom is a bytes calldata of any arbitrary size. This can be used as follows to break communication between chains that have different block gas limits.\\nExample: Let's say that an attacker wishes to permanently block the channel Arbitrum -> Optimism. Arbitrum has a massive gas block limit, much higher than Optimism's 20M block gas limit. The attacker would call sendFrom on the Arbitrum chain with the Optimism chain as the destination. For the _toAddress input they would use an absolutely massive amount of bytes. This would be packed into the payload which would be called on Optimism. Since Arbitrum has a huge gas limit the transaction would send from the Arbitrum side but it would be so big that the transaction could never succeed on the Optimism side due to gas constraints. Since that nonce can never succeed the communication channel will be permanently blocked at the Optimism endpoint, bypassing the nonblocking behavior implemented in the OFT design and reverting to the default blocking behavior of layerZero.\\nUsers can still send messages and burn their tokens from Arbitrum -> Optimism but the messages can never be received. This could be done between any two chain in which one has a higher block gas limit. This would cause massive loss of funds and completely cripple the entire protocol.

Impact:
Massive loss of user funds and protocol completely crippled

Recommended mitigation:
Limit the length of _toAddress to some amount (i.e. 256 bytes) as of right now EVM uses 20 bytes address and Sol/Aptos use 32 bytes address, so for right now it could be limited to 32 bytes.\\n```\\n    function sendFrom(address _from, uint16 _dstChainId, bytes calldata _toAddress, uint _amount, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) public payable virtual override {\\n+       require(_toAddress.length <= maxAddressLength);       \\n        _send(_from, _dstChainId, _toAddress, _amount, _refundAddress, _zroPaymentAddress, _adapterParams);\\n    }\\n```\\n
Vulnerability: RageTrade senior vault USDC deposits are subject to utilization caps which can lock deposits for long periods of time leading to UXD instability
Severity: high

Description:
RageTrade senior vault requires that it maintains deposits above and beyond the current amount loaned to the junior vault. Currently this is set at 90%, that is the vault must maintain at least 10% more deposits than loans. Currently the junior vault is in high demand and very little can be withdrawn from the senior vault. A situation like this is far from ideal because in the even that there is a strong depeg of UXD a large portion of the collateral could be locked in the vault unable to be withdrawn.\\nDnGmxSeniorVault.sol\\n```\\nfunction beforeWithdraw(\\n    uint256 assets,\\n    uint256,\\n    address\\n) internal override {\\n    /// @dev withdrawal will fail if the utilization goes above maxUtilization value due to a withdrawal\\n    // totalUsdcBorrowed will reduce when borrower (junior vault) repays\\n    if (totalUsdcBorrowed() > ((totalAssets() - assets) * maxUtilizationBps) / MAX_BPS)\\n        revert MaxUtilizationBreached();\\n\\n    // take out required assets from aave lending pool\\n    pool.withdraw(address(asset), assets, address(this));\\n}\\n```\\n\\nDnGmxSeniorVault.sol#beforeWithdraw is called before each withdraw and will revert if the withdraw lowers the utilization of the vault below a certain threshold. This is problematic in the event that large deposits are required to maintain the stability of UXD.

Impact:
UXD may become destabilized in the event that the senior vault has high utilization and the collateral is inaccessible

Recommended mitigation:
I recommend three safeguards against this:\\nMonitor the current utilization of the senior vault and limit deposits if utilization is close to locking positions\\nMaintain a portion of the USDC deposits outside the vault (i.e. 10%) to avoid sudden potential liquidity crunches\\nCreate functions to balance the proportions of USDC in and out of the vault to withdraw USDC from the vault in the event that utilization threatens to lock collateral
Vulnerability: `PerpDepository._rebalanceNegativePnlWithSwap()` shouldn't use a `sqrtPriceLimitX96` twice.
Severity: medium

Description:
`PerpDepository._rebalanceNegativePnlWithSwap()` shouldn't use a `sqrtPriceLimitX96` twice.\\nCurrently, `_rebalanceNegativePnlWithSwap()` uses a `sqrtPriceLimitX96` param twice for placing a perp order and swapping.\\n```\\n    function _rebalanceNegativePnlWithSwap(\\n        uint256 amount,\\n        uint256 amountOutMinimum,\\n        uint160 sqrtPriceLimitX96,\\n        uint24 swapPoolFee,\\n        address account\\n    ) private returns (uint256, uint256) {\\n        uint256 normalizedAmount = amount.fromDecimalToDecimal(\\n            ERC20(quoteToken).decimals(),\\n            18\\n        );\\n        _checkNegativePnl(normalizedAmount);\\n        bool isShort = false;\\n        bool amountIsInput = true;\\n        (uint256 baseAmount, uint256 quoteAmount) = _placePerpOrder(\\n            normalizedAmount,\\n            isShort,\\n            amountIsInput,\\n            sqrtPriceLimitX96\\n        );\\n        vault.withdraw(assetToken, baseAmount);\\n        SwapParams memory params = SwapParams({\\n            tokenIn: assetToken,\\n            tokenOut: quoteToken,\\n            amountIn: baseAmount,\\n            amountOutMinimum: amountOutMinimum,\\n            sqrtPriceLimitX96: sqrtPriceLimitX96, //@audit \\n            poolFee: swapPoolFee\\n        });\\n        uint256 quoteAmountOut = spotSwapper.swapExactInput(params);\\n```\\n\\nIn `_placePerpOrder()`, it uses the uniswap pool inside the perp protocol and uses a `spotSwapper` for the second swap which is for the uniswap as well.\\nBut as we can see here, Uniswap V3 introduces multiple pools for each token pair and 2 pools might be different and I think it's not good to use the same `sqrtPriceLimitX96` for different pools.\\nAlso, I think it's not mandatory to check a `sqrtPriceLimitX96` as it checks `amountOutMinimum` already. (It checks `amountOutMinimum` only in `_openLong()` and _openShort().)

Impact:
`PerpDepository._rebalanceNegativePnlWithSwap()` might revert when it should work as it uses the same `sqrtPriceLimitX96` for different pools.

Recommended mitigation:
I think we can use the `sqrtPriceLimitX96` param for one pool only and it would be enough as there is an `amountOutMinimum` condition.
Vulnerability: Vulnerable GovernorVotesQuorumFraction version
Severity: medium

Description:
The protocol uses an OZ version of contracts that contain a known vulnerability in government contracts.\\n`UXDGovernor` contract inherits from GovernorVotesQuorumFraction:\\n```\\n contract UXDGovernor is\\n    ReentrancyGuard,\\n    Governor,\\n    GovernorVotes,\\n    GovernorVotesQuorumFraction,\\n    GovernorTimelockControl,\\n    GovernorCountingSimple,\\n    GovernorSettings\\n```\\n\\nIt was patched in version 4.7.2, but this protocol uses an older version: "@openzeppelin/contracts": "^4.6.0"

Impact:
The potential impact is described in the OZ advisory. This issue was assigned with a severity of High from OZ, so I am sticking with it in this submission.

Recommended mitigation:
Update the OZ version of contracts to version >=4.7.2 or at least follow the workarounds of OZ if not possible otherwise.
Vulnerability: Deposit and withdraw to the vault with the wrong decimals of amount in contract `PerpDepository`
Severity: medium

Description:
Function `vault.deposit` and `vault.withdraw` of vault in contract `PerpDepository` need to be passed with the amount in raw decimal of tokens (is different from 18 in case using USDC, WBTC, ... as base and quote tokens). But some calls miss the conversion of decimals from 18 to token's decimal, and pass wrong decimals into them.\\nFunction `vault.deposit` need to be passed the param amount in token's decimal (as same as vault.withdraw). You can see at function `_depositAsset` in contract PerpDepository.\\n```\\nfunction _depositAsset(uint256 amount) private {\\n    netAssetDeposits += amount;\\n    \\n    IERC20(assetToken).approve(address(vault), amount);\\n    vault.deposit(assetToken, amount);\\n}\\n```\\n\\nBut there are some calls of `vault.deposit` and `vault.withdraw` that passed the amount in the wrong decimal (18 decimal). Let's see function `_rebalanceNegativePnlWithSwap` in contract PerpDepository:\\n```\\nfunction _rebalanceNegativePnlWithSwap(\\n    uint256 amount,\\n    uint256 amountOutMinimum,\\n    uint160 sqrtPriceLimitX96,\\n    uint24 swapPoolFee,\\n    address account\\n) private returns (uint256, uint256) {\\n    // rest of code\\n    (uint256 baseAmount, uint256 quoteAmount) = _placePerpOrder(\\n        normalizedAmount,\\n        isShort,\\n        amountIsInput,\\n        sqrtPriceLimitX96\\n    );\\n    vault.withdraw(assetToken, baseAmount); \\n    \\n    // rest of code\\n    \\n    vault.deposit(quoteToken, quoteAmount);\\n\\n    emit Rebalanced(baseAmount, quoteAmount, shortFall);\\n    return (baseAmount, quoteAmount);\\n}\\n```\\n\\nBecause function `_placePerpOrder` returns in decimal 18 (confirmed with sponsor WarTech), this calls pass `baseAmount` and `quoteAmount` in decimal 18, inconsistent with the above call. It leads to vault using the wrong decimal when depositing and withdrawing tokens.\\nThere is another case that use `vault.withdraw` with the wrong decimal (same as this case) in function _rebalanceNegativePnlLite:\\n```\\n//function _rebalanceNegativePnlLite, contract PerpDepository\\n// rest of code\\n\\n(uint256 baseAmount, uint256 quoteAmount) = _placePerpOrder(\\n    normalizedAmount,\\n    isShort,\\n    amountIsInput,\\n    sqrtPriceLimitX96\\n);\\nvault.withdraw(assetToken, baseAmount);\\n\\n// rest of code\\n```\\n

Impact:
Because of calling `vault.deposit` and `vault.withdraw` with the wrong decimal of the param amount, the protocol can lose a lot of funds. And some functionalities of the protocol can be broken cause it can revert by not enough allowance when calling these functions.

Recommended mitigation:
Should convert the param `amount` from token's decimal to decimal 18 before `vault.deposit` and `vault.withdraw`.
Vulnerability: Price disparities between spot and perpetual pricing can heavily destabilize UXD
Severity: medium

Description:
When minting UXD using PerpDepository.sol the amount of UXD minted corresponds to the amount of vUSD gained from selling the deposited ETH. This is problematic given that Perp Protocol is a derivative rather than a spot market, which means that price differences cannot be directly arbitraged with spot markets. The result is that derivative markets frequently trade at a price higher or lower than the spot price. The result of this is that UXD is actually pegged to vUSD rather than USD. This key difference can cause huge strain on a USD peg and likely depegging.\\n```\\nfunction deposit(\\n    address asset,\\n    uint256 amount\\n) external onlyController returns (uint256) {\\n    if (asset == assetToken) {\\n        _depositAsset(amount);\\n        (, uint256 quoteAmount) = _openShort(amount);\\n        return quoteAmount; // @audit this mint UXD equivalent to the amount of vUSD gained\\n    } else if (asset == quoteToken) {\\n        return _processQuoteMint(amount);\\n    } else {\\n        revert UnsupportedAsset(asset);\\n    }\\n}\\n```\\n\\nPerpDepository#deposit shorts the deposit amount and returns the amount of vUSD resulting from the swap, which effectively pegs it to vUSD rather than USD. When the perpetual is trading at a premium arbitrage will begin happening between the spot and perpetual asset and the profit will be taken at the expense of the UXD peg.\\nExample: Imagine markets are heavily trending with a spot price of $1500 and a perpetual price of $1530. A user can now buy 1 ETH for $1500 and deposit it to mint 1530 UXD. They can then swap the UXD for 1530 USDC (or other stablecoin) for a profit of $30. The user can continue to do this until either the perpetual price is arbitraged down to $1500 or the price of UXD is $0.98.

Impact:
UXD is pegged to vUSD rather than USD which can cause instability and loss of peg

Recommended mitigation:
I recommend integrating with a chainlink oracle and using its price to determine the true spot price of ETH. When a user mints make sure that the amount minted is never greater than the spot price of ETH which will prevent the negative pressure on the peg:\\n```\\nfunction deposit(\\n    address asset,\\n    uint256 amount\\n) external onlyController returns (uint256) {\\n    if (asset == assetToken) {\\n        _depositAsset(amount);\\n        (, uint256 quoteAmount) = _openShort(amount);\\n\\n+       spotPrice = assetOracle.getPrice();\\n+       assetSpotValue = amount.mulwad(spotPrice);\\n\\n-       return quoteAmount;\\n+       return quoteAmount <= assetSpotValue ? quoteAmount: assetSpotValue;\\n    } else if (asset == quoteToken) {\\n        return _processQuoteMint(amount);\\n    } else {\\n        revert UnsupportedAsset(asset);\\n    }\\n}\\n```\\n
Vulnerability: PerpDepository#_placePerpOrder miscalculates fees paid when shorting
Severity: medium

Description:
PerpDepository#_placePerpOrder calculates the fee as a percentage of the quoteToken received. The issue is that this amount already has the fees taken so the fee percentage is being applied incorrectly.\\n```\\nfunction _placePerpOrder(\\n    uint256 amount,\\n    bool isShort,\\n    bool amountIsInput,\\n    uint160 sqrtPriceLimit\\n) private returns (uint256, uint256) {\\n    uint256 upperBound = 0; // 0 = no limit, limit set by sqrtPriceLimit\\n\\n    IClearingHouse.OpenPositionParams memory params = IClearingHouse\\n        .OpenPositionParams({\\n            baseToken: market,\\n            isBaseToQuote: isShort, // true for short\\n            isExactInput: amountIsInput, // we specify exact input amount\\n            amount: amount, // collateral amount - fees\\n            oppositeAmountBound: upperBound, // output upper bound\\n            // solhint-disable-next-line not-rely-on-time\\n            deadline: block.timestamp,\\n            sqrtPriceLimitX96: sqrtPriceLimit, // max slippage\\n            referralCode: 0x0\\n        });\\n\\n    (uint256 baseAmount, uint256 quoteAmount) = clearingHouse.openPosition(\\n        params\\n    );\\n\\n    uint256 feeAmount = _calculatePerpOrderFeeAmount(quoteAmount);\\n    totalFeesPaid += feeAmount;\\n\\n    emit PositionOpened(isShort, amount, amountIsInput, sqrtPriceLimit);\\n    return (baseAmount, quoteAmount);\\n}\\n\\nfunction _calculatePerpOrderFeeAmount(uint256 amount)\\n    internal\\n    view\\n    returns (uint256)\\n{\\n    return amount.mulWadUp(getExchangeFeeWad());\\n}\\n```\\n\\nWhen calculating fees, `PerpDepository#_placePerpOrder` use the quote amount retuned when opening the new position. It always uses exactIn which means that for shorts the amount of baseAsset being sold is specified. The result is that quote amount returned is already less the fees. If we look at how the fee is calculated we can see that it is incorrect.\\nExample: Imagine the market price of ETH is $1000 and there is a market fee of 1%. The 1 ETH is sold and the contract receives 990 USD. Using the math above it would calculated the fee as $99 (990 * 1%) but actually the fee is $100.\\nIt have submitted this as a medium because it is not clear from the given contracts what the fee totals are used for and I cannot fully assess the implications of the fee value being incorrect.

Impact:
totalFeesPaid will be inaccurate which could lead to disparities in other contracts depending on how it is used

Recommended mitigation:
Rewrite _calculatePerpOrderFeeAmount to correctly calculate the fees paid:\\n```\\n-   function _calculatePerpOrderFeeAmount(uint256 amount)\\n+   function _calculatePerpOrderFeeAmount(uint256 amount, bool isShort)\\n        internal\\n        view\\n        returns (uint256)\\n    {\\n+       if (isShort) {\\n+           return amount.divWadDown(WAD - getExchangeFeeWad()) - amount;\\n+       } else {\\n            return amount.mulWadUp(getExchangeFeeWad());\\n+       }\\n    }\\n```\\n
Vulnerability: Boosted3TokenPoolUtils.sol : _redeem - updating the `totalBPTHeld , totalStrategyTokenGlobal` after `_unstakeAndExitPool` is not safe
Severity: medium

Description:
_redeem function is used to claim the BPT amount using the strategy tokens.\\nIt is first calling the `_unstakeAndExitPool` function and then updating the `totalBPTHeld , totalStrategyTokenGlobal`\\n```\\n    function _redeem(\\n    ThreeTokenPoolContext memory poolContext,\\n    StrategyContext memory strategyContext,\\n    AuraStakingContext memory stakingContext,\\n    uint256 strategyTokens,\\n    uint256 minPrimary\\n) internal returns (uint256 finalPrimaryBalance) {\\n    uint256 bptClaim = strategyContext._convertStrategyTokensToBPTClaim(strategyTokens);\\n\\n\\n    if (bptClaim == 0) return 0;\\n\\n\\n    finalPrimaryBalance = _unstakeAndExitPool({\\n        stakingContext: stakingContext,\\n        poolContext: poolContext,\\n        bptClaim: bptClaim,\\n        minPrimary: minPrimary\\n    });\\n\\n\\n    strategyContext.vaultState.totalBPTHeld -= bptClaim;\\n    strategyContext.vaultState.totalStrategyTokenGlobal -= strategyTokens.toUint80();\\n    strategyContext.vaultState.setStrategyVaultState(); \\n}\\n```\\n\\nFirst _unstakeAndExitPool is called and then totalBPTHeld and totalStrategyTokenGlobal are updated.

Impact:
Reentering during any of the function call inside `_unstakeAndExitPool` could be problematic. `stakingContext.auraRewardPool.withdrawAndUnwrap(bptClaim, false)` `BalancerUtils._swapGivenIn`\\nWell it need deep study to analyze the impact, but I would suggest to update the balance first and then call the `_unstakeAndExitPool`

Recommended mitigation:
First update `totalBPTHeld and totalStrategyTokenGlobal` and then call the `_unstakeAndExitPool`
Vulnerability: Unable to deploy new leverage vault for certain MetaStable Pool
Severity: medium

Description:
Notional might have an issue deploying the new leverage vault for a MetaStable Pool that does not have Balancer Oracle enabled.\\n```\\nFile: MetaStable2TokenVaultMixin.sol\\nabstract contract MetaStable2TokenVaultMixin is TwoTokenPoolMixin {\\n    constructor(NotionalProxy notional_, AuraVaultDeploymentParams memory params)\\n        TwoTokenPoolMixin(notional_, params)\\n    {\\n        // The oracle is required for the vault to behave properly\\n        (/* */, /* */, /* */, /* */, bool oracleEnabled) = \\n            IMetaStablePool(address(BALANCER_POOL_TOKEN)).getOracleMiscData();\\n        require(oracleEnabled);\\n    }\\n```\\n

Impact:
Notional might have an issue deploying the new leverage vault for a MetaStable Pool that does not have Balancer Oracle enabled. Since Balancer Oracle has been deprecated, the Balancer Oracle will likely be disabled on the MetaStable Pool.

Recommended mitigation:
Remove the Balancer Oracle check from the constructor.\\n```\\n    constructor(NotionalProxy notional_, AuraVaultDeploymentParams memory params)\\n        TwoTokenPoolMixin(notional_, params)\\n    {\\n// Remove the line below\\n        // The oracle is required for the vault to behave properly\\n// Remove the line below\\n        (/* */, /* */, /* */, /* */, bool oracleEnabled) = \\n// Remove the line below\\n            IMetaStablePool(address(BALANCER_POOL_TOKEN)).getOracleMiscData();\\n// Remove the line below\\n        require(oracleEnabled);\\n    }\\n```\\n
Vulnerability: Malicious user can DOS pool and avoid liquidation by creating secondary liquidity pool for Velodrome token pair
Severity: high

Description:
For every Vault_Velo interaction the vault attempts to price the liquidity of the user. This calls priceLiquidity in the corresponding DepsoitReciept. The prices the underlying assets by swapping them through the Velodrome router. Velodrome can have both a stable and volatile pool for each asset pair. When calling the router directly it routes through the pool that gives the best price. In priceLiquidity the transaction will revert if the router routes through the wrong pool (i.e. trading the volatile pool instead of the stable pool). A malicious user can use this to their advantage to avoid being liquidated. They could manipulate the price of the opposite pool so that any call to liquidate them would route through the wrong pool and revert.\\n```\\n    uint256 amountOut; //amount received by trade\\n    bool stablePool; //if the traded pool is stable or volatile.\\n    (amountOut, stablePool) = router.getAmountOut(HUNDRED_TOKENS, token1, USDC);\\n    require(stablePool == stable, "pricing occuring through wrong pool" );\\n```\\n\\nDepositReceipt uses the getAmountOut call the estimate the amountOut. The router will return the best rate between the volatile and stable pool. If the wrong pool give the better rate then the transaction will revert. Since pricing is called during liquidation, a malicious user could manipulate the price of the wrong pool so that it returns the better rate and always reverts the liquidation call.

Impact:
Malicious user can avoid liquidation

Recommended mitigation:
Instead of quoting from the router, query the correct pool directly:\\n```\\n        uint256 amountOut; //amount received by trade\\n-       bool stablePool; //if the traded pool is stable or volatile.\\n\\n-       (amountOut, stablePool) = router.getAmountOut(HUNDRED_TOKENS, token1, USDC);\\n-       require(stablePool == stable, "pricing occuring through wrong pool" );\\n+       address pair;\\n\\n+       pair = router.pairFor(token1, USDC, stable)\\n+       amountOut = IPair(pair).getAmountOut(HUNDRED_TOKENS, token1)\\n```\\n