msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: sponsorSeries() method fails when user want to swap for stake token using
Severity: medium

Description:
`sponsorSeries()` fails when user want to use `swapQuote` to swap for stake token to sponsor a series.\\nstake is token that user need to deposit (technically is pulled) to be able to sponsor a series for a given target. User has option to send `SwapQuote calldata quote` and swap any ERC20 token for stake token. Below is the code that doing transferFrom() of stakeToken not sellToken()\\n```\\nif (address(quote.sellToken) != ETH) _transferFrom(permit, stake, stakeSize);\\n if (address(quote.sellToken) != stake) _fillQuote(quote);\\n```\\n\\nExpected behaviour of this function is to pull `sellToken` from msg.sender when `address(quote.sellToken) != stake`. For example- stake token is WETH. User want to swap DAI for WETH in `sponsorSeries()`. In this case, user would be sending SwapQuote.sellToken = DAI and swapQuote.buyToke = WETH and expect that fillQuote() would swap it for WETH. This method will fail because `sellToken` not transferred from msg.sender.

Impact:
sponsorSeries() fails when `address(quote.sellToken) != stake`

Recommended mitigation:
Consider implementation of functionality to transferFrom `sellToken` from msg.sender with actual amount that is require to get exact amountOut greater or equal to `stakeSize`
Vulnerability: Refund of protocol fee is being to wrong user
Severity: medium

Description:
There is one function, _fillQuote(), which is handling swap from `0x`. Ideally If there is any remaining protocol fee (in ETH) then it will be returned to sender aka msg.sender. There are scenarios when fee can be sent to receiver of swap instead.\\nPeriphery and RollerPeriphery both are using almost identical logic in `_fillQuote()` hence this vulnerability affect both contracts. It exist if qupte.buyToken is ETH and there is any remaining protocol fee.\\nHere are pieces of puzzle\\nAfter swap if buyToken == ETH then store contract ETH balance in `boughtAmount`\\n```\\n// RollerPeriphery.sol\\n  boughtAmount = address(quote.buyToken) == ETH ? address(this).balance : quote.buyToken.balanceOf(address(this));\\n```\\n\\nNext it store refundAmt\\n```\\n// RollerPeriphery.sol\\n       // Refund any unspent protocol fees (paid in ether) to the sender.\\n       uint256 refundAmt = address(this).balance;\\n```\\n\\nCalculate actual refundAmt and transfer to sender\\n```\\n       if (address(quote.buyToken) == ETH) refundAmt = refundAmt - boughtAmount;\\n       payable(msg.sender).transfer(refundAmt);\\n```\\n\\nThis is clear that due to line 251, 258 and 259, refundAmt is 0. So sender is not getting refund.\\nLater on in logic flow buyToken will be transferred to receiver\\n```\\n       address(quote.buyToken) == ETH\\n               ? payable(receiver).transfer(amtOut)\\n               : ERC20(address(quote.buyToken)).safeTransfer(receiver, amtOut); // transfer bought tokens to receiver\\n```\\n

Impact:
Sender is not getting protocol fee refund.

Recommended mitigation:
Consider intercepting refund amount properly when buyToken is ETH or else just handle refund when buyToken is NOT ETH and write some explanation around it.
Vulnerability: FeeBuyback.submit() method may fail if all allowance is not used by referral contract
Severity: medium

Description:
Inside `submit()` method of `FeeBuyback.sol`, if token is `_telcoin` then it safeApprove to `_referral` contract. If `_referral` contract do not use all allowance then `submit()` method will fail in next call.\\n`SafeApprove()` method of library `SafeERC20Upgradeable` revert in following scenario.\\n```\\nrequire((value == 0) || (token.allowance(address(this), spender) == 0), \\n"SafeERC20: approve from non-zero to non-zero allowance");\\n```\\n\\nSubmit method is doing `safeApproval` of Telcoin to referral contract. If referral contract do not use full allowance then subsequent call to `submit()` method will fails because of `SafeERC20: approve from non-zero to non-zero allowance`. `FeeBuyback` contract should not trust or assume that referral contract will use all allowance. If it does not use all allowance in `increaseClaimableBy()` method then `submit()` method will revert in next call. This vulnerability exists at two places in `submit()` method. Link given in code snippet section.

Impact:
Submit() call will fail until referral contract do not use all allowance.

Recommended mitigation:
Reset allowance to 0 before non-zero approval.\\n```\\n_telcoin.safeApprove(address(_referral), 0);\\n_telcoin.safeApprove(address(_referral), _telcoin.balanceOf(address(this)));\\n```\\n
Vulnerability: Missing input validation for _rewardProportion parameter allows keeper to escalate his privileges and pay back all loans
Severity: high

Description:
They are also able to choose how much yield token to swap and what the proportion of the resulting TAU is that is distributed to users vs. not distributed in order to erase bad debt.\\nSo a `keeper` is not trusted to perform any actions that go beyond swapping yield / performing liquidations.\\nHowever there is a missing input validation for the `_rewardProportion` parameter in the `SwapHandler.swapForTau` function. This allows a keeper to "erase" all debt of users. So users can withdraw their collateral without paying any of the debt.\\nBy looking at the code we can see that `_rewardProportion` is used to determine the amount of `TAU` that `_withholdTau` is called with: Link\\n```\\n_withholdTau((tauReturned * _rewardProportion) / Constants.PERCENT_PRECISION);\\n```\\n\\nAny value of `_rewardProportion` greater than `1e18` means that more `TAU` will be distributed to users than has been burnt (aka erasing debt).\\nIt is easy to see how the `keeper` can chose the number so big that `_withholdTau` is called with a value close to `type(uint256).max` which will certainly be enough to erase all debt.

Impact:
A `keeper` can escalate his privileges and erase all debt. This means that `TAU` will not be backed by any collateral anymore and will be worthless.

Recommended mitigation:
I discussed this issue with the sponsor and it is intended that the `keeper` role can freely chose the value of the `_rewardProportion` parameter within the `[0,1e18]` range, i.e. 0%-100%.\\nTherefore the fix is to simply check that `_rewardProportion` is not bigger than 1e18:\\n```\\ndiff --git a/taurus-contracts/contracts/Vault/SwapHandler.sol b/taurus-contracts/contracts/Vault/SwapHandler.sol\\nindex c04e3a4..ab5064b 100644\\n--- a/taurus-contracts/contracts/Vault/SwapHandler.sol\\n// Add the line below\\n// Add the line below\\n// Add the line below\\n b/taurus-contracts/contracts/Vault/SwapHandler.sol\\n@@ -59,6 // Add the line below\\n59,10 @@ abstract contract SwapHandler is FeeMapping, TauDripFeed {\\n             revert zeroAmount();\\n         }\\n \\n// Add the line below\\n        if (_rewardProportion > Constants.PERCENT_PRECISION) [\\n// Add the line below\\n            revert invalidRewardProportion();\\n// Add the line below\\n        ]\\n// Add the line below\\n\\n         // Get and validate swap adapter address\\n         address swapAdapterAddress = SwapAdapterRegistry(controller).swapAdapters(_swapAdapterHash);\\n         if (swapAdapterAddress == address(0)) {\\n```\\n
Vulnerability: `swap()` will be reverted if `path` has more tokens.
Severity: medium

Description:
`swap()` will be reverted if `path` has more tokens, the keepers will not be able to successfully call `swapForTau()`.\\nIn test/SwapAdapters/00_UniswapSwapAdapter.ts:\\n```\\n    // Get generic swap parameters\\n    const basicSwapParams = buildUniswapSwapAdapterData(\\n      ["0xyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "0xzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"],\\n      [3000],\\n      testDepositAmount,\\n      expectedReturnAmount,\\n      0,\\n    ).swapData;\\n```\\n\\nWe will get:\\n```\\n000000000000000000000000000000000000000000000000000000024f49cbca\\n0000000000000000000000000000000000000000000000056bc75e2d63100000\\n0000000000000000000000000000000000000000000000055de6a779bbac0000\\n0000000000000000000000000000000000000000000000000000000000000080\\n000000000000000000000000000000000000000000000000000000000000002b\\nyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy000bb8zzzzzzzzzzzzzzzzzz\\nzzzzzzzzzzzzzzzzzzzzzz000000000000000000000000000000000000000000\\n```\\n\\nThen the `swapOutputToken` is `_swapData[length - 41:length - 21]`.\\nBut if we have more tokens in path:\\n```\\n    const basicSwapParams = buildUniswapSwapAdapterData(\\n      ["0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "0xyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "0xzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"],\\n      [3000, 3000],\\n      testDepositAmount,\\n      expectedReturnAmount,\\n      0,\\n    ).swapData;\\n```\\n\\n```\\n000000000000000000000000000000000000000000000000000000024f49cbca\\n0000000000000000000000000000000000000000000000056bc75e2d63100000\\n0000000000000000000000000000000000000000000000055de6a779bbac0000\\n0000000000000000000000000000000000000000000000000000000000000080\\n0000000000000000000000000000000000000000000000000000000000000042\\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx000bb8yyyyyyyyyyyyyyyyyy\\nyyyyyyyyyyyyyyyyyyyyyy000bb8zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\\nzzzz000000000000000000000000000000000000000000000000000000000000\\n```\\n\\n`swapOutputToken` is `_swapData[length - 50:length - 30]`, the `swap()` function will be reverted.

Impact:
The keepers will not be able to successfully call `SwapHandler.swapForTau()`. Someone will get a reverted transaction if they misuse `UniswapSwapAdapter`.

Recommended mitigation:
Limit the swap pools, or check if the balance of `_outputToken` should exceed `_amountOutMinimum`.
Vulnerability: Mint limit is not reduced when the Vault is burning TAU
Severity: medium

Description:
Upon burning TAU, it incorrectly updates the `currentMinted` when Vault is acting on behalf of users.\\nWhen the burn of `TAU` is performed, it calls `_decreaseCurrentMinted` to reduce the limit of tokens minted by the Vault:\\n```\\n    function _decreaseCurrentMinted(address account, uint256 amount) internal virtual {\\n        // If the burner is a vault, subtract burnt TAU from its currentMinted.\\n        // This has a few highly unimportant edge cases which can generally be rectified by increasing the relevant vault's mintLimit.\\n        uint256 accountMinted = currentMinted[account];\\n        if (accountMinted >= amount) {\\n            currentMinted[msg.sender] = accountMinted - amount;\\n        }\\n    }\\n```\\n\\nThe issue is that it subtracts `accountMinted` (which is currentMinted[account]) from `currentMinted[msg.sender]`. When the vault is burning tokens on behalf of the user, the `account` != `msg.sender` meaning the `currentMinted[account]` is 0, and thus the `currentMinted` of Vault will be reduced by 0 making it pretty useless.\\nAnother issue is that users can transfer their `TAU` between accounts, and then `amount > accountMinted` will not be triggered.

Impact:
`currentMinted` is incorrectly decreased upon burning so vaults do not get more space to mint new tokens.

Recommended mitigation:
A simple solution would be to:\\n```\\n     uint256 accountMinted = currentMinted[msg.sender];\\n```\\n\\nBut I suggest revisiting and rethinking this function altogether.
Vulnerability: Account can not be liquidated when price fall by 99%.
Severity: medium

Description:
Liquidation fails when price fall by 99%.\\n`_calcLiquidation()` method has logic related to liquidations. This method calculate total liquidation discount, collateral to liquidate and liquidation surcharge. All these calculations looks okay in normal scenarios but there is an edge case when liquidation fails if price crashes by 99% or more. In such scenario `collateralToLiquidateWithoutDiscount` will be very large and calculated liquidation surcharge becomes greater than `collateralToLiquidate`\\n```\\nuint256 collateralToLiquidateWithoutDiscount = (_debtToLiquidate * (10 ** decimals)) / price;\\ncollateralToLiquidate = (collateralToLiquidateWithoutDiscount * totalLiquidationDiscount) / Constants.PRECISION;\\nif (collateralToLiquidate > _accountCollateral) {\\n            collateralToLiquidate = _accountCollateral;\\n}\\nuint256 liquidationSurcharge = (collateralToLiquidateWithoutDiscount * LIQUIDATION_SURCHARGE) / Constants.PRECISION\\n```\\n\\nContract revert from below line hence liquidation will fail in this scenario.\\n```\\nuint256 collateralToLiquidator = collateralToLiquidate - liquidationSurcharge;\\n```\\n

Impact:
Liquidation fails when price crash by 99% or more. Expected behaviour is that liquidation should be successful in all scenarios.

Recommended mitigation:
Presently liquidation surcharge is calculated on `collateralToLiquidateWithoutDiscount`. Project team may want to reconsider this logic and calculate surcharge on `collateralToLiquidate` instead of `collateralToLiquidateWithoutDiscount`. This will be business decision but easy fix\\nAnother option is you may want to calculate surcharge on `Math.min(collateralToLiquidate, collateralToLiquidateWithoutDiscount)`.\\n```\\n        uint256 collateralToTakeSurchargeOn = Math.min(collateralToLiquidate, collateralToLiquidateWithoutDiscount);\\n        uint256 liquidationSurcharge = (collateralToTakeSurchargeOn * LIQUIDATION_SURCHARGE) / Constants.PRECISION;\\n        return (collateralToLiquidate, liquidationSurcharge);\\n```\\n
Vulnerability: Protocol is will not work on most of the supported blockchains due to hardcoded WETH contract address.
Severity: medium

Description:
The WETH address is hardcoded in the `Swap` library.\\nAs stated in the README.md, the protocol will be deployed on the following EVM blockchains - Ethereum Mainnet, Arbitrum, Optimism, Polygon, Binance Smart Chain. While the project has integration tests with an ethereum mainnet RPC, they don't catch that on different chains like for example Polygon saveral functionallities will not actually work because of the hardcoded WETH address in the Swap.sol library:\\n```\\naddress internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\\n```\\n

Impact:
Protocol will not work on most of the supported blockchains.

Recommended mitigation:
The WETH variable should be immutable in the Vault contract instead of a constant in the Swap library and the Wrapped Native Token contract address should be passed in the Vault constructor on each separate deployment.
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:
If user specific limit is required then transfer should be check below:\\n```\\n require(_amountTransferred + balanceRecepient <= maxTrainingDeposit);\\n```\\n
Vulnerability: Vault.blacklistProtocol can revert in emergency
Severity: medium

Description:
Vault.blacklistProtocol can revert in emergency, because it tries to withdraw underlying balance from protocol, which can revert for many reasons after it's hacked or paused.\\n```\\n  function blacklistProtocol(uint256 _protocolNum) external onlyGuardian {\\n    uint256 balanceProtocol = balanceUnderlying(_protocolNum);\\n    currentAllocations[_protocolNum] = 0;\\n    controller.setProtocolBlacklist(vaultNumber, _protocolNum);\\n    savedTotalUnderlying -= balanceProtocol;\\n    withdrawFromProtocol(_protocolNum, balanceProtocol);\\n  }\\n```\\n\\nThe problem is that this function is trying to withdraw all balance from protocol. This can create problems as in case of hack, attacker can steal funds, pause protocol and any other things that can make `withdrawFromProtocol` function to revert. Because of that it will be not possible to add protocol to blacklist and as result system will stop working correctly.

Impact:
Hacked or paused protocol can't be set to blacklist.

Recommended mitigation:
Provide `needToWithdraw` param to the `blacklistProtocol` function. In case if it's safe to withdraw, then withdraw, otherwise, just set protocol as blacklisted. Also you can call function with `true` param again, once it's safe to withdraw. Example of hack situation flow: 1.underlying vault is hacked 2.you call setProtocolBlacklist("vault", false) which blacklists vault 3.in next tx you call setProtocolBlacklist("vault", true) and tries to withdraw
Vulnerability: The protocol could not handle multiple vaults correctly
Severity: medium

Description:
The protocol needs to handle multiple vaults correctly. If there are three vaults (e.g.USDC, USDT, DAI) the protocol needs to rebalance them all without any problems\\nThe protocol needs to invoke pushAllocationsToController() every `rebalanceInterval` to push totalDeltaAllocations from Game to xChainController.\\n`pushAllocationsToController()` invoke `rebalanceNeeded()` to check if a rebalance is needed based on the set interval and it uses the state variable `lastTimeStamp` to do the calculations\\n```\\n  function rebalanceNeeded() public view returns (bool) {\\n    return (block.timestamp - lastTimeStamp) > rebalanceInterval || msg.sender == guardian;\\n  }\\n```\\n\\nBut in the first invoking (for USDC vault) of `pushAllocationsToController()` it will update the state variable `lastTimeStamp` to the current `block.timestamp`\\n```\\nlastTimeStamp = block.timestamp;\\n```\\n\\nNow when you invoke (for DAI vault) `pushAllocationsToController()`. It will revert because of\\n```\\nrequire(rebalanceNeeded(), "No rebalance needed");\\n```\\n\\nSo if the protocol has two vaults or more (USDC, USDT, DAI) you can only do one rebalance every `rebalanceInterval`

Impact:
The protocol could not handle multiple vaults correctly\\nBoth Users and Game players will lose funds because the MainVault will not rebalance the protocols at the right time with the right values

Recommended mitigation:
Keep tracking the `lastTimeStamp` for every `_vaultNumber` by using an array
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:
In case if protocol is blacklisted, then set `rewardPerLockedToken` to 0 inside `storePriceAndRewards` function.
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:
Issue Malicious users could set allocations to a blacklist Protocol and break the rebalancing logic\\nYou should check if the Protocol on the blacklist when Game players `rebalanceBasket()`
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:
Issue Wrong calculation of `balanceBefore` and `balanceAfter` in deposit method\\nUse 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