msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: Reuse of CHAINID from contract deployment
Severity: low

Description:
The internal function `_validateWithdrawSignature()` is used to check whether a sponsored token withdrawal is approved by the owner of the stealth address that received the tokens. Among other data, the chain ID is signed over to prevent replay of signatures on other EVM-compatible chains.\\n```\\nfunction \\_validateWithdrawSignature(\\n  address \\_stealthAddr,\\n  address \\_acceptor,\\n  address \\_tokenAddr,\\n  address \\_sponsor,\\n  uint256 \\_sponsorFee,\\n  IUmbraHookReceiver \\_hook,\\n  bytes memory \\_data,\\n  uint8 \\_v,\\n  bytes32 \\_r,\\n  bytes32 \\_s\\n) internal view {\\n  bytes32 \\_digest =\\n    keccak256(\\n      abi.encodePacked(\\n        "\\x19Ethereum Signed Message:\\n32",\\n        keccak256(abi.encode(chainId, version, \\_acceptor, \\_tokenAddr, \\_sponsor, \\_sponsorFee, address(\\_hook), \\_data))\\n      )\\n    );\\n\\n  address \\_recoveredAddress = ecrecover(\\_digest, \\_v, \\_r, \\_s);\\n  require(\\_recoveredAddress != address(0) && \\_recoveredAddress == \\_stealthAddr, "Umbra: Invalid Signature");\\n}\\n```\\n\\nHowever, this chain ID is set as an immutable value in the contract constructor. In the case of a future contentious hard fork of the Ethereum network, the same `Umbra` contract would exist on both of the resulting chains. One of these two chains would be expected to change the network's chain ID, but the `Umbra` contracts would not be aware of this change. As a result, signatures to the `Umbra` contract on either chain would be replayable on the other chain.\\nThis is a common pattern in contracts that implement EIP-712 signatures. Presumably, the motivation in most cases for committing to the chain ID at deployment time is to avoid recomputing the EIP-712 domain separator for every signature verification. In this case, the chain ID is a direct input to the generation of the signed digest, so this should not be a concern.

Impact:

Recommended mitigation:
Replace the use of the `chainId` immutable value with the `CHAINID` opcode in `_validateWithdrawSignature()`. Note that `CHAINID` is only available using Solidity's inline assembly, so this would need to be accessed in the same way as it is currently accessed in the contract's constructor:\\n```\\nuint256 \\_chainId;\\n\\nassembly {\\n  \\_chainId := chainid()\\n}\\n```\\n
Vulnerability: Random task execution
Severity: high

Description:
In a scenario where user takes a flash loan, `_parseFLAndExecute()` gives the flash loan wrapper contract (FLAaveV2, FLDyDx) the permission to execute functions on behalf of the user's `DSProxy`. This execution permission is revoked only after the entire recipe execution is finished, which means that in case that any of the external calls along the recipe execution is malicious, it might call `executeAction()` back and inject any task it wishes (e.g. take user's funds out, drain approved tokens, etc)\\n```\\nfunction executeOperation(\\n    address[] memory \\_assets,\\n    uint256[] memory \\_amounts,\\n    uint256[] memory \\_fees,\\n    address \\_initiator,\\n    bytes memory \\_params\\n) public returns (bool) {\\n    require(msg.sender == AAVE\\_LENDING\\_POOL, ERR\\_ONLY\\_AAVE\\_CALLER);\\n    require(\\_initiator == address(this), ERR\\_SAME\\_CALLER);\\n\\n    (Task memory currTask, address proxy) = abi.decode(\\_params, (Task, address));\\n\\n    // Send FL amounts to user proxy\\n    for (uint256 i = 0; i < \\_assets.length; ++i) {\\n        \\_assets[i].withdrawTokens(proxy, \\_amounts[i]);\\n    }\\n\\n    address payable taskExecutor = payable(registry.getAddr(TASK\\_EXECUTOR\\_ID));\\n\\n    // call Action execution\\n    IDSProxy(proxy).execute{value: address(this).balance}(\\n        taskExecutor,\\n        abi.encodeWithSelector(CALLBACK\\_SELECTOR, currTask, bytes32(\\_amounts[0] + \\_fees[0]))\\n    );\\n\\n    // return FL\\n    for (uint256 i = 0; i < \\_assets.length; i++) {\\n        \\_assets[i].approveToken(address(AAVE\\_LENDING\\_POOL), \\_amounts[i] + \\_fees[i]);\\n    }\\n\\n    return true;\\n}\\n```\\n

Impact:

Recommended mitigation:
A reentrancy guard (mutex) that covers the entire content of FLAaveV2.executeOperation/FLDyDx.callFunction should be used to prevent such attack.
Vulnerability: Tokens with more than 18 decimal points will cause issues
Severity: high

Description:
It is assumed that the maximum number of decimals for each token is 18. However uncommon, but it is possible to have tokens with more than 18 decimals, as an Example YAMv2 has 24 decimals. This can result in broken code flow and unpredictable outcomes (e.g. an underflow will result with really high rates).\\n```\\n    function getSellRate(address \\_srcAddr, address \\_destAddr, uint \\_srcAmount, bytes memory) public override view returns (uint rate) {\\n        (rate, ) = KyberNetworkProxyInterface(KYBER\\_INTERFACE)\\n            .getExpectedRate(IERC20(\\_srcAddr), IERC20(\\_destAddr), \\_srcAmount);\\n\\n        // multiply with decimal difference in src token\\n        rate = rate \\* (10\\*\\*(18 - getDecimals(\\_srcAddr)));\\n        // divide with decimal difference in dest token\\n        rate = rate / (10\\*\\*(18 - getDecimals(\\_destAddr)));\\n    }\\n```\\n

Impact:

Recommended mitigation:
Make sure the code won't fail in case the token's decimals is more than 18.
Vulnerability: Error codes of Compound's Comptroller.enterMarket, Comptroller.exitMarket are not checked
Severity: high

Description:
Compound's `enterMarket/exitMarket` functions return an error code instead of reverting in case of failure. DeFi Saver smart contracts never check for the error codes returned from Compound smart contracts, although the code flow might revert due to unavailability of the CTokens, however early on checks for Compound errors are suggested.\\n```\\nfunction enterMarket(address \\_cTokenAddr) public {\\n    address[] memory markets = new address[](1);\\n    markets[0] = \\_cTokenAddr;\\n\\n    IComptroller(COMPTROLLER\\_ADDR).enterMarkets(markets);\\n}\\n\\n/// @notice Exits the Compound market\\n/// @param \\_cTokenAddr CToken address of the token\\nfunction exitMarket(address \\_cTokenAddr) public {\\n    IComptroller(COMPTROLLER\\_ADDR).exitMarket(\\_cTokenAddr);\\n}\\n```\\n

Impact:

Recommended mitigation:
Caller contract should revert in case the error code is not 0.
Vulnerability: Reversed order of parameters in allowance function call
Severity: medium

Description:
When trying to pull the maximum amount of tokens from an approver to the allowed spender, the parameters that are used for the `allowance` function call are not in the same order that is used later in the call to `safeTransferFrom`.\\n```\\nfunction pullTokens(\\n    address \\_token,\\n    address \\_from,\\n    uint256 \\_amount\\n) internal returns (uint256) {\\n    // handle max uint amount\\n    if (\\_amount == type(uint256).max) {\\n        uint256 allowance = IERC20(\\_token).allowance(address(this), \\_from);\\n        uint256 balance = getBalance(\\_token, \\_from);\\n\\n        \\_amount = (balance > allowance) ? allowance : balance;\\n    }\\n\\n    if (\\_from != address(0) && \\_from != address(this) && \\_token != ETH\\_ADDR && \\_amount != 0) {\\n        IERC20(\\_token).safeTransferFrom(\\_from, address(this), \\_amount);\\n    }\\n\\n    return \\_amount;\\n}\\n```\\n

Impact:

Recommended mitigation:
Reverse the order of parameters in `allowance` function call to fit the order that is in the `safeTransferFrom` function call.
Vulnerability: Kyber getRates code is unclear
Severity: low

Description:
`getSellRate` can be converted into one function to get the rates, which then for buy or sell can swap input and output tokens\\n`getBuyRate` uses a 3% slippage that is not documented.\\n```\\n   function getSellRate(address \\_srcAddr, address \\_destAddr, uint \\_srcAmount, bytes memory) public override view returns (uint rate) {\\n        (rate, ) = KyberNetworkProxyInterface(KYBER\\_INTERFACE)\\n            .getExpectedRate(IERC20(\\_srcAddr), IERC20(\\_destAddr), \\_srcAmount);\\n\\n        // multiply with decimal difference in src token\\n        rate = rate \\* (10\\*\\*(18 - getDecimals(\\_srcAddr)));\\n        // divide with decimal difference in dest token\\n        rate = rate / (10\\*\\*(18 - getDecimals(\\_destAddr)));\\n    }\\n\\n    /// @notice Return a rate for which we can buy an amount of tokens\\n    /// @param \\_srcAddr From token\\n    /// @param \\_destAddr To token\\n    /// @param \\_destAmount To amount\\n    /// @return rate Rate\\n    function getBuyRate(address \\_srcAddr, address \\_destAddr, uint \\_destAmount, bytes memory \\_additionalData) public override view returns (uint rate) {\\n        uint256 srcRate = getSellRate(\\_destAddr, \\_srcAddr, \\_destAmount, \\_additionalData);\\n        uint256 srcAmount = wmul(srcRate, \\_destAmount);\\n\\n        rate = getSellRate(\\_srcAddr, \\_destAddr, srcAmount, \\_additionalData);\\n\\n        // increase rate by 3% too account for inaccuracy between sell/buy conversion\\n        rate = rate + (rate / 30);\\n    }\\n```\\n

Impact:

Recommended mitigation:
Refactoring the code to separate getting rate functionality with `getSellRate` and `getBuyRate`. Explicitly document any assumptions in the code ( slippage, etc)
Vulnerability: Return values not used for DFSExchangeCore.onChainSwap
Severity: low

Description:
Return values from `DFSExchangeCore.onChainSwap` are not used.\\n```\\nfunction \\_sell(ExchangeData memory exData) internal returns (address, uint256) {\\n    uint256 amountWithoutFee = exData.srcAmount;\\n    address wrapper = exData.offchainData.wrapper;\\n    bool offChainSwapSuccess;\\n\\n    uint256 destBalanceBefore = exData.destAddr.getBalance(address(this));\\n\\n    // Takes DFS exchange fee\\n    exData.srcAmount -= getFee(\\n        exData.srcAmount,\\n        exData.user,\\n        exData.srcAddr,\\n        exData.dfsFeeDivider\\n    );\\n\\n    // Try 0x first and then fallback on specific wrapper\\n    if (exData.offchainData.price > 0) {\\n        (offChainSwapSuccess, ) = offChainSwap(exData, ExchangeActionType.SELL);\\n    }\\n\\n    // fallback to desired wrapper if 0x failed\\n    if (!offChainSwapSuccess) {\\n        onChainSwap(exData, ExchangeActionType.SELL);\\n        wrapper = exData.wrapper;\\n    }\\n\\n    uint256 destBalanceAfter = exData.destAddr.getBalance(address(this));\\n    uint256 amountBought = sub(destBalanceAfter, destBalanceBefore);\\n\\n    // check slippage\\n    require(amountBought >= wmul(exData.minPrice, exData.srcAmount), ERR\\_SLIPPAGE\\_HIT);\\n\\n    // revert back exData changes to keep it consistent\\n    exData.srcAmount = amountWithoutFee;\\n\\n    return (wrapper, amountBought);\\n}\\n```\\n\\n```\\nfunction \\_buy(ExchangeData memory exData) internal returns (address, uint256) {\\n    require(exData.destAmount != 0, ERR\\_DEST\\_AMOUNT\\_MISSING);\\n\\n    uint256 amountWithoutFee = exData.srcAmount;\\n    address wrapper = exData.offchainData.wrapper;\\n    bool offChainSwapSuccess;\\n\\n    uint256 destBalanceBefore = exData.destAddr.getBalance(address(this));\\n\\n    // Takes DFS exchange fee\\n    exData.srcAmount -= getFee(\\n        exData.srcAmount,\\n        exData.user,\\n        exData.srcAddr,\\n        exData.dfsFeeDivider\\n    );\\n\\n    // Try 0x first and then fallback on specific wrapper\\n    if (exData.offchainData.price > 0) {\\n        (offChainSwapSuccess, ) = offChainSwap(exData, ExchangeActionType.BUY);\\n    }\\n\\n    // fallback to desired wrapper if 0x failed\\n    if (!offChainSwapSuccess) {\\n        onChainSwap(exData, ExchangeActionType.BUY);\\n        wrapper = exData.wrapper;\\n    }\\n\\n    uint256 destBalanceAfter = exData.destAddr.getBalance(address(this));\\n    uint256 amountBought = sub(destBalanceAfter, destBalanceBefore);\\n\\n    // check slippage\\n    require(amountBought >= exData.destAmount, ERR\\_SLIPPAGE\\_HIT);\\n\\n    // revert back exData changes to keep it consistent\\n    exData.srcAmount = amountWithoutFee;\\n\\n    return (wrapper, amountBought);\\n}\\n```\\n

Impact:

Recommended mitigation:
The return value can be used for verification of the swap or used in the event data.
Vulnerability: Return value is not used for TokenUtils.withdrawTokens
Severity: low

Description:
The return value of `TokenUtils.withdrawTokens` which represents the actual amount of tokens that were transferred is never used throughout the repository. This might cause discrepancy in the case where the original value of `_amount` was `type(uint256).max`.\\n```\\nfunction \\_borrow(\\n    address \\_market,\\n    address \\_tokenAddr,\\n    uint256 \\_amount,\\n    uint256 \\_rateMode,\\n    address \\_to,\\n    address \\_onBehalf\\n) internal returns (uint256) {\\n    ILendingPoolV2 lendingPool = getLendingPool(\\_market);\\n\\n    // defaults to onBehalf of proxy\\n    if (\\_onBehalf == address(0)) {\\n        \\_onBehalf = address(this);\\n    }\\n\\n    lendingPool.borrow(\\_tokenAddr, \\_amount, \\_rateMode, AAVE\\_REFERRAL\\_CODE, \\_onBehalf);\\n\\n    \\_tokenAddr.withdrawTokens(\\_to, \\_amount);\\n\\n    logger.Log(\\n        address(this),\\n        msg.sender,\\n        "AaveBorrow",\\n        abi.encode(\\_market, \\_tokenAddr, \\_amount, \\_rateMode, \\_to, \\_onBehalf)\\n    );\\n\\n    return \\_amount;\\n}\\n```\\n\\n```\\nfunction withdrawTokens(\\n    address \\_token,\\n    address \\_to,\\n    uint256 \\_amount\\n) internal returns (uint256) {\\n    if (\\_amount == type(uint256).max) {\\n        \\_amount = getBalance(\\_token, address(this));\\n    }\\n```\\n

Impact:

Recommended mitigation:
The return value can be used to validate the withdrawal or used in the event emitted.
Vulnerability: Anyone is able to mint NFTs by calling mintNFTsForLM
Severity: high

Description:
The contract `LiquidityMiningNFT` has the method `mintNFTsForLM`.\\n```\\nfunction mintNFTsForLM(address \\_liquidiyMiningAddr) external {\\n    uint256[] memory \\_ids = new uint256[](NFT\\_TYPES\\_COUNT);\\n    uint256[] memory \\_amounts = new uint256[](NFT\\_TYPES\\_COUNT);\\n\\n    \\_ids[0] = 1;\\n    \\_amounts[0] = 5;\\n\\n    \\_ids[1] = 2;\\n    \\_amounts[1] = 1 \\* LEADERBOARD\\_SIZE;\\n\\n    \\_ids[2] = 3;\\n    \\_amounts[2] = 3 \\* LEADERBOARD\\_SIZE;\\n\\n    \\_ids[3] = 4;\\n    \\_amounts[3] = 6 \\* LEADERBOARD\\_SIZE;\\n\\n    \\_mintBatch(\\_liquidiyMiningAddr, \\_ids, \\_amounts, "");\\n}\\n```\\n\\nHowever, this contract does not have any kind of special permissions to limit who is able to mint tokens.\\nAn attacker could call `LiquidityMiningNFT.mintNFTsForLM(0xhackerAddress)` to mint tokens for their address and sell them on the marketplace. They are also allowed to mint as many tokens as they want by calling the method multiple times.

Impact:

Recommended mitigation:
Add some permissions to limit only some actors to mint tokens.
Vulnerability: The buyPolicyFor/addLiquidityFor should transfer funds from msg.sender
Severity: high

Description:
When calling the buyPolicyFor/addLiquidityFor functions, are called with the parameter _policyHolderAddr/_liquidityHolderAddr who is going to be the beneficiary in buying policy/adding liquidity:\\n```\\nfunction buyPolicyFor(\\n  address \\_policyHolderAddr,\\n  uint256 \\_epochsNumber,\\n  uint256 \\_coverTokens   \\n) external override {\\n  \\_buyPolicyFor(\\_policyHolderAddr, \\_epochsNumber, \\_coverTokens);\\n}\\n```\\n\\n```\\nfunction addLiquidityFor(address \\_liquidityHolderAddr, uint256 \\_liquidityAmount) external override {\\n  \\_addLiquidityFor(\\_liquidityHolderAddr, \\_liquidityAmount, false);\\n}\\n```\\n\\nDuring the execution, the funds for the policy/liquidity are transferred from the _policyHolderAddr/_liquidityHolderAddr, while it's usually expected that they should be transferred from `msg.sender`. Because of that, anyone can call a function on behalf of a user that gave the allowance to the `PolicyBook`.\\nFor example, a user(victim) wants to add some DAI to the liquidity pool and gives allowance to the `PolicyBook`. After that, the user should call `addLiquidity`, but the attacker can front-run this transaction and buy a policy on behalf of the victim instead.\\nAlso, there is a curious edge case that makes this issue Critical: _policyHolderAddr/_liquidityHolderAddr parameters can be equal to the address of the `PolicyBook` contract. That may lead to multiple different dangerous attack vectors.

Impact:

Recommended mitigation:
Make sure that nobody can transfer funds on behalf of the users if it's not intended.
Vulnerability: LiquidityMining can't accept single ERC1155 tokens
Severity: high

Description:
The contract `LiquidityMining` is also defined as an `ERC1155Receiver`\\n```\\ncontract LiquidityMining is ILiquidityMining, ERC1155Receiver, Ownable {\\n```\\n\\nThe finalized EIP-1155 standard states that a contract which acts as an EIP-1155 Receiver must implement all the functions in the `ERC1155TokenReceiver` interface to be able to accept transfers.\\nThese are indeed implemented here:\\n```\\nfunction onERC1155Received(\\n```\\n\\n```\\nfunction onERC1155BatchReceived(\\n```\\n\\nThe standard states that they will be called and they MUST return a specific `byte4` value, otherwise the transfer will fail.\\nHowever one of the methods returns an incorrect value. This seems to an error generated by a copy/paste action.\\n```\\nfunction onERC1155Received(\\n    address operator,\\n    address from,\\n    uint256 id,\\n    uint256 value,\\n    bytes memory data\\n)\\n    external\\n    pure\\n    override\\n    returns(bytes4)\\n{\\n    return bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"));\\n}\\n```\\n\\nThe value returned is equal to\\n`bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"));`\\nBut it should be\\n`bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.\\nOn top of this, the contract MUST implement the ERC-165 standard to correctly respond to `supportsInterface`.

Impact:

Recommended mitigation:
Change the return value of `onERC1155Received` to be equal to `0xf23a6e61` which represents `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.\\nAlso, make sure to implement `supportsInterface` to signify support of `ERC1155TokenReceiver` to accept transfers.\\nAdd tests to check the functionality is correct and make sure these kinds of bugs do not exist in the future.\\nMake sure to read the EIP-1155 and EIP-165 standards in detail and implement them correctly.
Vulnerability: DAI is assumed to have the same price as DAIx in the staking contract
Severity: high

Description:
When a liquidity provider stakes tokens to the `BMIDAIStaking` contract, the equal amount of DAI and DAIx are transferred from the pool contract.\\n```\\nfunction \\_stakeDAIx(address \\_user, uint256 \\_amount, address \\_policyBookAddr) internal {\\n    require (\\_amount > 0, "BMIDAIStaking: Can't stake zero tokens");\\n\\n    PolicyBook \\_policyBook = PolicyBook(\\_policyBookAddr);\\n    // transfer DAI from PolicyBook to yield generator\\n    daiToken.transferFrom(\\_policyBookAddr, address(defiYieldGenerator), \\_amount);            \\n\\n    // transfer bmiDAIx from user to staking\\n    \\_policyBook.transferFrom(\\_user, address(this), \\_amount);       \\n\\n    \\_mintNFT(\\_user, \\_amount, \\_policyBook);\\n}\\n```\\n

Impact:

Recommended mitigation:
Only the corresponding amount of DAI should be transferred to the pool.
Vulnerability: _updateWithdrawalQueue can run out of gas
Severity: high

Description:
When there's not enough collateral to withdraw liquidity from a policy book, the withdrawal request is added to a queue. The queue is supposed to be processed and cleared once there are enough funds for that. The only way to do so is the `_updateWithdrawalQueue` function that is caller when new liquidity is added:\\n```\\nfunction \\_updateWithdrawalQueue() internal {\\n  uint256 \\_availableLiquidity = totalLiquidity.sub(totalCoverTokens);\\n  uint256 \\_countToRemoveFromQueue;\\n\\n  for (uint256 i = 0; i < withdrawalQueue.length; i++) {     \\n    uint256 \\_tokensToWithdraw = withdrawalsInfo[withdrawalQueue[i]].amount;\\n    uint256 \\_amountInDai = \\_tokensToWithdraw.mul(getDAIToDAIxRatio()).div(PERCENTAGE\\_100);\\n\\n    if (balanceOf(withdrawalQueue[i]) < \\_tokensToWithdraw) {\\n      \\_countToRemoveFromQueue++;\\n      continue;\\n    }\\n\\n    if (\\_availableLiquidity >= \\_amountInDai) {\\n      \\_withdrawLiquidity(withdrawalQueue[i], \\_tokensToWithdraw);\\n      \\_availableLiquidity = \\_availableLiquidity.sub(\\_amountInDai);\\n      \\_countToRemoveFromQueue++;\\n    } else {\\n      break;\\n    }\\n  }\\n\\n  \\_removeFromQueue(\\_countToRemoveFromQueue);\\n}\\n```\\n\\nThe problem is that this function can only process all queue until the pool run out of available funds or the whole queue is going to be processed. If the queue is big enough, this process can be stuck.

Impact:

Recommended mitigation:
Pass the parameter to the `_updateWithdrawalQueue` that defines how many requests to process in the queue per one call.
Vulnerability: The PolicyBook should make DAI transfers inside the contract
Severity: medium

Description:
The `PolicyBook` contract gives full allowance over DAI tokens to the other contracts:\\n```\\nfunction approveAllDaiTokensForStakingAndVotingAndTransferOwnership() internal {\\n  daiToken.approve(address(bmiDaiStaking), MAX\\_INT);   \\n  daiToken.approve(address(claimVoting), MAX\\_INT);    \\n\\n  transferOwnership(address(bmiDaiStaking));\\n}\\n```\\n\\nThat behavior is dangerous because it's hard to keep track of and control the contract's DAI balance. And it's also hard to track in the code where the balance of the `PolicyBook` can be changed from.

Impact:

Recommended mitigation:
It's better to perform all the transfers inside the `PolicyBook` contract. So if the `bmiDaiStaking` and the `claimVoting` contracts need DAI tokens from the `PolicyBook`, they should call some function of the `PolicyBook` to perform transfers.
Vulnerability: The totalCoverTokens is only updated when the policy is bought
Severity: medium

Description:
The `totalCoverTokens` value represents the amount of collateral that needs to be locked in the policy book. It should be changed either by buying a new policy or when an old policy expires. The problem is that when the old policy expires, this value is not updated; it is only updated when someone buys a policy by calling the `_updateEpochsInfo` function:\\n```\\nfunction \\_updateEpochsInfo() internal {\\n  uint256 \\_totalEpochTime = block.timestamp.sub(epochStartTime);\\n  uint256 \\_countOfPassedEpoch = \\_totalEpochTime.div(epochDuration);\\n\\n  uint256 \\_lastEpochUpdate = currentEpochNumber;\\n  currentEpochNumber = \\_countOfPassedEpoch.add(1);\\n\\n  for (uint256 i = \\_lastEpochUpdate; i < currentEpochNumber; i++) {\\n    totalCoverTokens = totalCoverTokens.sub(epochAmounts[i]);\\n    delete epochAmounts[i];\\n  }\\n}\\n```\\n\\nUsers waiting to withdraw liquidity should wait for someone to buy the policy to update the `totalCoverTokens`.

Impact:

Recommended mitigation:
Resolution\\nThe `updateEpochsInfo` function is now public and can be called by anyone.\\nMake sure it's possible to call the `_updateEpochsInfo` function without buying a new policy.
Vulnerability: Unbounded loops in LiquidityMining
Severity: medium

Description:
There are some methods that have unbounded loops and will fail when enough items exist in the arrays.\\n```\\nfor (uint256 i = 0; i < \\_teamsNumber; i++) {\\n```\\n\\n```\\nfor (uint256 i = 0; i < \\_membersNumber; i++) {\\n```\\n\\n```\\nfor (uint256 i = 0; i < \\_usersNumber; i++) {\\n```\\n\\nThese methods will fail when lots of items will be added to them.

Impact:

Recommended mitigation:
Consider adding limits (from, to) when requesting the items.