msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Vault could `rebalance()` before funds arrive from xChainController
Severity: medium

Description:
Invoke sendFundsToVault() to Push funds from xChainController to vaults. which is call xTransferToVaults()\\nFor the cross-chain rebalancing `xTransferToVaults()` will execute this logic\\n```\\n       // rest of code\\n      pushFeedbackToVault(_chainId, _vault, _relayerFee);\\n      xTransfer(_asset, _amount, _vault, _chainId, _slippage, _relayerFee);\\n       // rest of code\\n```\\n\\n`pushFeedbackToVault()` Is to invoke receiveFunds() `pushFeedbackToVault()` always travel through the slow path\\n`xTransfer()` to transfer funds from one chain to another If fast liquidity is not available, the `xTransfer()` will go through the slow path.\\nThe vulnerability is if the `xcall()` of `pushFeedbackToVault()` excited successfully before `xTransfer()` transfer the funds to the vault, anyone can invoke rebalance() this will lead to rebalancing Vaults with Imperfect funds (this could be true only if funds that are expected to be received from XChainController are greater than `reservedFunds` and `liquidityPerc` together )\\nThe above scenario could be done in two possible cases 1- `xTransfer()` will go through the slow path but because High Slippage the cross-chain message will wait until slippage conditions improve (relayers will continuously re-attempt the transfer execution).\\n2- Connext Team says\\n```\\nAll messages are added to a Merkle root which is sent across chains every 30 mins\\nAnd then those messages are executed by off-chain actors called routers\\n\\nso it is indeed possible that messages are received out of order (and potentially with increased latency in between due to batch times) \\nFor "fast path" (unauthenticated) messages, latency is not a concern, but ordering may still be (this is an artifact of the chain itself too btw)\\none thing you can do is add a nonce to your messages so that you can yourself order them at destination\\n```\\n\\nso `pushFeedbackToVault()` and `xTransfer()` could be added to a different Merkle root and this will lead to executing `receiveFunds()` before funds arrive.

Impact:
The vault could `rebalance()` before funds arrive from xChainController, this will reduce rewards

Recommended mitigation:
Check if funds are arrived or not
Vulnerability: Wrong type casting leads to unsigned integer underflow exception when current price is < last price
Severity: medium

Description:
When the current price of a locked token is lower than the last price, the Vault.storePriceAndRewards will revert because of the wrong integer casting.\\nThe following line appears in Vault.storePriceAndRewards:\\n```\\nint256 priceDiff = int256(currentPrice - lastPrices[_protocolId]);\\n```\\n\\nIf lastPrices[_protocolId] is higher than the currentPrice, the solidity compiler will revert due the underflow of subtracting unsigned integers because it will first try to calculate the result of `currentPrice - lastPrices[_protocolId]` and then try to cast it to int256.

Impact:
The rebalance will fail when the current token price is less than the last one stored.

Recommended mitigation:
Casting should be performed in the following way to avoid underflow and to allow the priceDiff being negative:\\n```\\nint256 priceDiff = int256(currentPrice) - int256(lastPrices[_protocolId]));\\n```\\n
Vulnerability: withdrawal request override
Severity: medium

Description:
It is possible that a withdrawal request is overridden during the initial phase.\\nUsers have two options to withdraw: directly or request a withdrawal if not enough funds are available at the moment.\\nWhen making a `withdrawalRequest` it is required that the user has `withdrawalRequestPeriod` not set:\\n```\\n  function withdrawalRequest(\\n    uint256 _amount\\n  ) external nonReentrant onlyWhenVaultIsOn returns (uint256 value) {\\n    UserInfo storage user = userInfo[msg.sender];\\n    require(user.withdrawalRequestPeriod == 0, "Already a request");\\n\\n    value = (_amount * exchangeRate) / (10 ** decimals());\\n\\n    _burn(msg.sender, _amount);\\n\\n    user.withdrawalAllowance = value;\\n    user.withdrawalRequestPeriod = rebalancingPeriod;\\n    totalWithdrawalRequests += value;\\n  }\\n```\\n\\nThis will misbehave during the initial period when `rebalancingPeriod` is 0. The check will pass, so if invoked multiple times, it will burn users' shares and overwrite the value.

Impact:
While not very likely to happen, the impact would be huge, because the users who invoke this function several times before the first rebalance, would burn their shares and lose previous `withdrawalAllowance`. The protocol should prevent such mistakes.

Recommended mitigation:
Require `rebalancingPeriod` != 0 in `withdrawalRequest`, otherwise, force users to directly withdraw.
Vulnerability: Wrong type casting leads to unsigned integer underflow exception when current price is < last price
Severity: high

Description:
When the current price of a locked token is lower than the last price, the Vault.storePriceAndRewards will revert because of the wrong integer casting.\\nThe following line appears in Vault.storePriceAndRewards:\\n```\\nint256 priceDiff = int256(currentPrice - lastPrices[_protocolId]);\\n```\\n\\nIf lastPrices[_protocolId] is higher than the currentPrice, the solidity compiler will revert due the underflow of subtracting unsigned integers because it will first try to calculate the result of `currentPrice - lastPrices[_protocolId]` and then try to cast it to int256.

Impact:
The rebalance will fail when the current token price is less than the last one stored.

Recommended mitigation:
Casting should be performed in the following way to avoid underflow and to allow the priceDiff being negative:\\n```\\nint256 priceDiff = int256(currentPrice) - int256(lastPrices[_protocolId]));\\n```\\n
Vulnerability: Not all providers claim the rewards
Severity: high

Description:
Providers wrongly assume that the protocols will no longer incentivize users with extra rewards.\\nAmong the current providers only the `CompoundProvider` claims the `COMP` incentives, others leave the claim function empty:\\n```\\n  function claim(address _aToken, address _claimer) public override returns (bool) {}\\n```\\n

Impact:
The implementations of the providers are based on the current situation. They are not flexible enough to support the rewards in case the incentives are back.

Recommended mitigation:
Adjust the providers to be ready to claim the rewards if necessary.
Vulnerability: Wrong calculation of `balanceBefore` and `balanceAfter` in deposit method
Severity: medium

Description:
Deposit method calculate net amount transferred from user. It use `reservedFunds` also in consideration when calculating `balanceBefore` and `balanceAfter` but it is not actually require.\\n```\\n    uint256 balanceBefore = getVaultBalance() - reservedFunds;\\n    vaultCurrency.safeTransferFrom(msg.sender, address(this), _amount);\\n    uint256 balanceAfter = getVaultBalance() - reservedFunds;\\n    uint256 amount = balanceAfter - balanceBefore;\\n```\\n\\nDeposit may fail when `reservedFunds` is greater than `getVaultBalance()`

Impact:
Deposit may fail when `reservedFunds` is greater than `getVaultBalance()`

Recommended mitigation:
Use below code. This is correct way of finding net amount transfer by depositor\\n```\\n    uint256 balanceBefore = getVaultBalance();\\n    vaultCurrency.safeTransferFrom(msg.sender, address(this), _amount);\\n    uint256 balanceAfter = getVaultBalance();\\n    uint256 amount = balanceAfter - balanceBefore;\\n```\\n
Vulnerability: Malicious users could set allocations to a blacklist Protocol and break the rebalancing logic
Severity: medium

Description:
`game.sol` pushes `deltaAllocations` to vaults by pushAllocationsToVaults() and it deletes all the value of the `deltas`\\n```\\nvaults[_vaultNumber].deltaAllocationProtocol[_chainId][i] = 0;\\n```\\n\\nMalicious users could set allocations to a blacklist Protocol. If only one of the `Baskets` has a non-zero value to a Protocol on blacklist receiveProtocolAllocations() will revert `receiveProtocolAllocations().receiveProtocolAllocationsInt().setDeltaAllocationsInt()`\\n```\\n  function setDeltaAllocationsInt(uint256 _protocolNum, int256 _allocation) internal {\\n    require(!controller.getProtocolBlacklist(vaultNumber, _protocolNum), "Protocol on blacklist");\\n    deltaAllocations[_protocolNum] += _allocation;\\n    deltaAllocatedTokens += _allocation;\\n  }\\n```\\n\\nand You won't be able to execute rebalance()

Impact:
The guardian isn't able to restart the protocol manually. `game.sol` loses the value of the `deltas`. The whole system is down.

Recommended mitigation:
You should check if the Protocol on the blacklist when Game players `rebalanceBasket()`
Vulnerability: Asking for `balanceOf()` in the wrong address
Severity: medium

Description:
on sendFundsToVault() this logic\\n```\\naddress underlying = getUnderlyingAddress(_vaultNumber, _chain);\\nuint256 balance = IERC20(underlying).balanceOf(address(this));\\n```\\n\\nin case `_chainId` is Optimism the `underlying` address is for Optimism (L2) but `XChainController` is on Mainnet you can't invoke `balanceOf()` like this!!!

Impact:
Asking for `balanceOf()` in the wrong address The protocol will be not able to rebalance the vault

Recommended mitigation:
Issue Asking for `balanceOf()` in the wrong address\\n`getUnderlyingAddress(_vaultNumber, _chain);` should just be `getUnderlyingAddress(_vaultNumber);` so the `underlying` here\\n```\\nuint256 balance = IERC20(underlying).balanceOf(address(this));\\n```\\n\\nwill be always on the Mainnet
Vulnerability: `getDecimals()` always call the MainNet
Severity: medium

Description:
`XChainController.pushVaultAmounts()` is to push `exchangeRate` to the vaults. `XChainController.getVaultAddress()` returns the vault address of `vaultNumber` with the given `chainID`\\n`pushVaultAmounts()` invoke `xProvider.getDecimals()` internally to calculate `newExchangeRate`\\nThe xProvider.getDecimals() is always call the `address(vault)` from the MainNet. but `address(vault)` could be in any chain `XChainController.pushVaultAmounts()` could keep reverting with all the `chainID` (only the MainNet will be correct ) or it will return the wrong `decimals` values. (if the `address(vault)` is for other chain/L but it exist in the MainNet with a decimals())\\nthis will lead to a wrong `newExchangeRate`\\n```\\nuint256 newExchangeRate = (totalUnderlying * (10 ** decimals)) / totalSupply;\\n```\\n

Impact:
`pushVaultAmounts()` will keep reverting and this will break all rebalancing logic

Recommended mitigation:
You should invoke `getVaultAddress()` with `_chain` of the Mainnet. because all vaults have the same getDecimals (not all vaultNamber)
Vulnerability: User should not receive rewards for the rebalance period, when protocol was blacklisted, because of unpredicted behaviour of protocol price
Severity: medium

Description:
User should not receive rewards for the rebalance period, when protocol was blacklisted, because of unpredicted behaviour of protocol price.\\nWhen user allocates derby tokens to some underlying protocol, he receive rewards according to the exchange price of that protocols token. This reward can be positive or negative. Rewards of protocol are set to `Game` contract inside `settleRewards` function and they are accumulated for user, once he calls `rebalanceBasket`.\\n```\\n  function storePriceAndRewards(uint256 _totalUnderlying, uint256 _protocolId) internal {\\n    uint256 currentPrice = price(_protocolId);\\n    if (lastPrices[_protocolId] == 0) {\\n      lastPrices[_protocolId] = currentPrice;\\n      return;\\n    }\\n\\n\\n    int256 priceDiff = int256(currentPrice - lastPrices[_protocolId]);\\n    int256 nominator = (int256(_totalUnderlying * performanceFee) * priceDiff);\\n    int256 totalAllocatedTokensRounded = totalAllocatedTokens / 1E18;\\n    int256 denominator = totalAllocatedTokensRounded * int256(lastPrices[_protocolId]) * 100; // * 100 cause perfFee is in percentages\\n\\n\\n    if (totalAllocatedTokensRounded == 0) {\\n      rewardPerLockedToken[rebalancingPeriod][_protocolId] = 0;\\n    } else {\\n      rewardPerLockedToken[rebalancingPeriod][_protocolId] = nominator / denominator;\\n    }\\n\\n\\n    lastPrices[_protocolId] = currentPrice;\\n  }\\n```\\n\\nEvery time, previous price of protocol is compared with current price.\\nIn case if some protocol is hacked, there is `Vault.blacklistProtocol` function, that should withdraw reserves from protocol and mark it as blacklisted. The problem is that because of the hack it's not possible to determine what will happen with exhange rate of protocol. It can be 0, ot it can be very small or it can be high for any reasons. But protocol still accrues rewards per token for protocol, even that it is blacklisted. Because of that, user that allocated to that protocol can face with accruing very big negative or positive rewards. Both this cases are bad.\\nSo i believe that in case if protocol is blacklisted, it's better to set rewards as 0 for it.\\nExample. 1.User allocated 100 derby tokens for protocol A 2.Before `Vault.rebalance` call, protocol A was hacked which made it exchangeRate to be not real. 3.Derby team has blacklisted that protocol A. 4.Vault.rebalance is called which used new(incorrect) exchangeRate of protocol A in order to calculate `rewardPerLockedToken` 5.When user calls rebalance basket next time, his rewards are accumulated with extremely high/low value.

Impact:
User's rewards calculation is unpredictable.

Recommended mitigation:
Issue User should not receive rewards for the rebalance period, when protocol was blacklisted, because of unpredicted behaviour of protocol price\\nIn case if protocol is blacklisted, then set `rewardPerLockedToken` to 0 inside `storePriceAndRewards` function.
Vulnerability: maxTrainingDeposit can be bypassed
Severity: medium

Description:
It was observed that User can bypass the `maxTrainingDeposit` by transferring balance from one user to another\\nObserve the `deposit` function\\n```\\nfunction deposit(\\n    uint256 _amount,\\n    address _receiver\\n  ) external nonReentrant onlyWhenVaultIsOn returns (uint256 shares) {\\n    if (training) {\\n      require(whitelist[msg.sender]);\\n      uint256 balanceSender = (balanceOf(msg.sender) * exchangeRate) / (10 ** decimals());\\n      require(_amount + balanceSender <= maxTrainingDeposit);\\n    }\\n// rest of code\\n```\\n\\nSo if User balance exceeds maxTrainingDeposit then request fails (considering training is true)\\nLets say User A has balance of 50 and maxTrainingDeposit is 100\\nIf User A deposit amount 51 then it fails since 50+51<=100 is false\\nSo User A transfer amount 50 to his another account\\nNow when User A deposit, it does not fail since `0+51<=100`

Impact:
User can bypass maxTrainingDeposit and deposit more than allowed

Recommended mitigation:
Issue maxTrainingDeposit can be bypassed\\nIf user specific limit is required then transfer should be check below:\\n```\\n require(_amountTransferred + balanceRecepient <= maxTrainingDeposit);\\n```\\n
Vulnerability: `oracleSlippagePercentOrLimit` can exceed the `Constants.SLIPPAGE_LIMIT_PRECISION`
Severity: medium

Description:
Trade might be settled with a large slippage causing a loss of assets as the `oracleSlippagePercentOrLimit` limit is not bounded and can exceed the `Constants.SLIPPAGE_LIMIT_PRECISION` threshold.\\nThe code at Line 73-75 only checks if the `oracleSlippagePercentOrLimit` is within the `Constants.SLIPPAGE_LIMIT_PRECISION` if `useDynamicSlippage` is `true`. If the trade is performed without dynamic slippage, the trade can be executed with an arbitrary limit.\\n```\\nFile: StrategyUtils.sol\\n    function _executeTradeExactIn(\\n        TradeParams memory params,\\n        ITradingModule tradingModule,\\n        address sellToken,\\n        address buyToken,\\n        uint256 amount,\\n        bool useDynamicSlippage\\n    ) internal returns (uint256 amountSold, uint256 amountBought) {\\n        require(\\n            params.tradeType == TradeType.EXACT_IN_SINGLE || params.tradeType == TradeType.EXACT_IN_BATCH\\n        );\\n        if (useDynamicSlippage) {\\n            require(params.oracleSlippagePercentOrLimit <= Constants.SLIPPAGE_LIMIT_PRECISION);\\n        }\\n\\n        // Sell residual secondary balance\\n        Trade memory trade = Trade(\\n            params.tradeType,\\n            sellToken,\\n            buyToken,\\n            amount,\\n            useDynamicSlippage ? 0 : params.oracleSlippagePercentOrLimit,\\n            block.timestamp, // deadline\\n            params.exchangeData\\n        );\\n```\\n\\nThe `StrategyUtils._executeTradeExactIn` function is utilized by the Curve Vault.

Impact:
Trade might be settled with a large slippage causing a loss of assets.

Recommended mitigation:
Consider restricting the slippage limit when a trade is executed without dynamic slippage.\\n```\\n    function _executeTradeExactIn(\\n        TradeParams memory params,\\n        ITradingModule tradingModule,\\n        address sellToken,\\n        address buyToken,\\n        uint256 amount,\\n        bool useDynamicSlippage\\n    ) internal returns (uint256 amountSold, uint256 amountBought) {\\n        require(\\n            params.tradeType == TradeType.EXACT_IN_SINGLE || params.tradeType == TradeType.EXACT_IN_BATCH\\n        );\\n        if (useDynamicSlippage) {\\n            require(params.oracleSlippagePercentOrLimit <= Constants.SLIPPAGE_LIMIT_PRECISION);\\n// Remove the line below\\n       }\\n// Add the line below\\n  } else {\\n// Add the line below\\n    require(params.oracleSlippagePercentOrLimit != 0 && params.oracleSlippagePercentOrLimit <= Constants.SLIPPAGE_LIMIT_PRECISION_FOR_NON_DYNAMIC_TRADE);\\n// Add the line below\\n   }     \\n```\\n
Vulnerability: Signers can bypass checks and change threshold within a transaction
Severity: high

Description:
The `checkAfterExecution()` function has checks to ensure that the safe's threshold isn't changed by a transaction executed by signers. However, the parameters used by the check can be changed midflight so that this crucial restriction is violated.\\nThe `checkAfterExecution()` is intended to uphold important invariants after each signer transaction is completed. This is intended to restrict certain dangerous signer behaviors. From the docs:\\n/// @notice Post-flight check to prevent `safe` signers from removing this contract guard, changing any modules, or changing the threshold\\nHowever, the restriction that the signers cannot change the threshold can be violated.\\nTo see how this is possible, let's check how this invariant is upheld. The following check is performed within the function:\\n```\\nif (safe.getThreshold() != _getCorrectThreshold()) {\\n    revert SignersCannotChangeThreshold();\\n}\\n```\\n\\nIf we look up `_getCorrectThreshold()`, we see the following:\\n```\\nfunction _getCorrectThreshold() internal view returns (uint256 _threshold) {\\n    uint256 count = _countValidSigners(safe.getOwners());\\n    uint256 min = minThreshold;\\n    uint256 max = targetThreshold;\\n    if (count < min) _threshold = min;\\n    else if (count > max) _threshold = max;\\n    else _threshold = count;\\n}\\n```\\n\\nAs we can see, this means that the safe's threshold after the transaction must equal the valid signers, bounded by the `minThreshold` and `maxThreshold`.\\nHowever, this check does not ensure that the value returned by `_getCorrectThreshold()` is the same before and after the transaction. As a result, as long as the number of owners is also changed in the transaction, the condition can be upheld.\\nTo illustrate, let's look at an example:\\nBefore the transaction, there are 8 owners on the vault, all signers. targetThreshold == 10 and minThreshold == 2, so the safe's threshold is 8 and everything is good.\\nThe transaction calls `removeOwner()`, removing an owner from the safe and adjusting the threshold down to 7.\\nAfter the transaction, there will be 7 owners on the vault, all signers, the safe's threshold will be 7, and the check will pass.\\nThis simple example focuses on using `removeOwner()` once to decrease the threshold. However, it is also possible to use the safe's multicall functionality to call `removeOwner()` multiple times, changing the threshold more dramatically.

Impact:
Signers can change the threshold of the vault, giving themselves increased control over future transactions and breaking an important trust assumption of the protocol.

Recommended mitigation:
Save the safe's current threshold in `checkTransaction()` before the transaction has executed, and compare the value after the transaction to that value from storage.
Vulnerability: Other module can add owners to safe that push us above maxSigners, bricking safe
Severity: high

Description:
If another module adds owners to the safe, these additions are not checked by our module or guard's logic. This can result in pushing us over `maxSigners`, which will cause all transactions to revert. In the case of an immutable hat, the only way to avoid the safe being locked permanently (with all funds frozen) may be to convince many hat wearers to renounce their hats.\\nWhen new owners are added to the contract through the `claimSigner()` function, the total number of owners is compared to `maxSigners` to ensure it doesn't exceed it.\\nHowever, if there are other modules on the safe, they are able to add additional owners without these checks.\\nIn the case of `HatsSignerGate.sol`, there is no need to call `claimSigner()` to "activate" these owners. They will automatically be valid as long as they are a wearer of the correct hat.\\nThis could lead to an issue where many (more than maxSigners) wearers of an immutable hat are added to the safe as owners. Now, each time a transaction is processed, `checkTransaction()` is called, which calls `reconcileSignerCount()`, which has the following check:\\n```\\nif (validSignerCount > maxSigners) {\\n    revert MaxSignersReached();\\n}\\n```\\n\\nThis will revert.\\nWorse, there is nothing the admin can do about it. If they don't have control over the eligibility address for the hat, they are not able to burn the hats or transfer them.\\nThe safe will be permanently bricked and unable to perform transactions unless the hat wearers agree to renounce their hats.

Impact:
The safe can be permanently bricked and unable to perform transactions unless the hat wearers agree to renounce their hats.

Recommended mitigation:
If `validSignerCount > maxSigners`, there should be some mechanism to reduce the number of signers rather than reverting.\\nAlternatively, as suggested in another issue, to get rid of all the potential risks of having other modules able to make changes outside of your module's logic, we should create the limit that the HatsSignerGate module can only exist on a safe with no other modules.