msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: AuraSpell#closePositionFarm exits pool with single token and without any slippage protection
Severity: medium

Description:
AuraSpell.sol#L221-L236\\n```\\n            (\\n                uint256[] memory minAmountsOut,\\n                address[] memory tokens,\\n                uint256 borrowTokenIndex\\n            ) = _getExitPoolParams(param.borrowToken, lpToken);\\n\\n            wAuraPools.getVault(lpToken).exitPool(\\n                IBalancerPool(lpToken).getPoolId(),\\n                address(this),\\n                address(this),\\n                IBalancerVault.ExitPoolRequest(\\n                    tokens,\\n                    minAmountsOut,\\n                    abi.encode(0, amountPosRemove, borrowTokenIndex),\\n                    false\\n                )\\n```\\n\\nWhen exiting a the balancer vault, closePositionFarm makes a subcall to _getExitPoolParams which is used to set minAmountsOut.\\nAuraSpell.sol#L358-L361\\n```\\n    (address[] memory tokens, , ) = wAuraPools.getPoolTokens(lpToken);\\n\\n    uint256 length = tokens.length;\\n    uint256[] memory minAmountsOut = new uint256[](length);\\n```\\n\\nInside _getExitPoolParams we see that minAmountsOut are always an empty array. This means that the user has no slippage protection and can be sandwich attacked, suffering massive losses.

Impact:
Exits can be sandwich attacked causing massive loss to the user

Recommended mitigation:
Allow user to specify min amount received from exit
Vulnerability: AuraSpell#closePositionFarm will take reward fees on underlying tokens when borrow token is also a reward
Severity: medium

Description:
AuraSpell.sol#L227-L247\\n```\\n            wAuraPools.getVault(lpToken).exitPool(\\n                IBalancerPool(lpToken).getPoolId(),\\n                address(this),\\n                address(this),\\n                IBalancerVault.ExitPoolRequest(\\n                    tokens,\\n                    minAmountsOut,\\n                    abi.encode(0, amountPosRemove, borrowTokenIndex),\\n                    false\\n                )\\n            );\\n        }\\n    }\\n\\n    /// 4. Swap each reward token for the debt token\\n    uint256 rewardTokensLength = rewardTokens.length;\\n    for (uint256 i; i != rewardTokensLength; ) {\\n        address sellToken = rewardTokens[i];\\n        if (sellToken == STASH_AURA) sellToken = AURA;\\n\\n        _doCutRewardsFee(sellToken);\\n```\\n\\nWe can see above that closePositionFarm redeems the BLP before it takes the reward cut. This can cause serious issues. If there is any overlap between the reward tokens and the borrow token then _doCutRewardsFee will take a cut of the underlying liquidity. This causes loss to the user as too many fees are taken from them.

Impact:
User will lose funds due to incorrect fees

Recommended mitigation:
Use the same order as ConvexSpell and sell rewards BEFORE burning BLP
Vulnerability: Adversary can abuse hanging approvals left by PSwapLib.swap to bypass reward fees
Severity: medium

Description:
AuraSpell.sol#L247-L257\\n```\\n        _doCutRewardsFee(sellToken);\\n        if (\\n            expectedRewards[i] != 0 &&\\n            !PSwapLib.swap(\\n                augustusSwapper,\\n                tokenTransferProxy,\\n                sellToken,\\n                expectedRewards[i],\\n                swapDatas[i]\\n            )\\n        ) revert Errors.SWAP_FAILED(sellToken);\\n```\\n\\nAuraSpell#closePositionFarm allows the user to specify any expectedRewards they wish. This allows the user to approve any amount, even if the amount is much larger than they would otherwise use. The can abuse these hanging approvals to swap tokens out of order and avoid paying reward fees.\\nExample: Assume there are two rewards, token A and token B. Over time a user's position accumulates 100 rewards for each token. Normally the user would have to pay fees on those rewards. However they can bypass it by first creating hanging approvals. The user would start by redeeming a very small amount of LP and setting expectedRewards to uint256.max. They wouldn't sell the small amount leaving a very large approval left for both tokens. Now the user withdraws the rest of their position. This time they specify the swap data to swap token B first. The user still has to pay fees on token A but now they have traded token B before any fees can be taken on it.

Impact:
User can bypass reward fees

Recommended mitigation:
After the swap reset allowances to 0
Vulnerability: ConvexSpell is completely broken for any curve LP that utilizes native ETH
Severity: medium

Description:
ConvexSpell.sol#L120-L127\\n```\\n    if (tokens.length == 2) {\\n        uint256[2] memory suppliedAmts;\\n        for (uint256 i; i != 2; ++i) {\\n            suppliedAmts[i] = IERC20Upgradeable(tokens[i]).balanceOf(\\n                address(this)\\n            );\\n        }\\n        ICurvePool(pool).add_liquidity(suppliedAmts, minLPMint);\\n```\\n\\nConvexSpell#openPositionFarm attempts to call balanceOf on each component of the LP. Since native ETH uses the `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee` this call will always revert. This breaks compatibility with EVERY curve pool that uses native ETH which make most of the highest volume pools on the platfrom.

Impact:
ConvexSpell is completely incompatible with a majority of Curve pools

Recommended mitigation:
I would recommend conversion between native ETH and wETH to prevent this issue.
Vulnerability: WAuraPools doesn't correctly account for AuraStash causing all deposits to be permanently lost
Severity: medium

Description:
WAuraPools.sol#L413-L418\\n```\\n    uint256 rewardTokensLength = rewardTokens.length;\\n    for (uint256 i; i != rewardTokensLength; ) {\\n        IERC20Upgradeable(rewardTokens[i]).safeTransfer(\\n            msg.sender,\\n            rewards[i]\\n        );\\n```\\n\\nWhen burning the wrapped LP token, it attempts to transfer each token to msg.sender. The problem is that stash AURA cannot be transferred like an regular ERC20 token and any transfers will revert. Since this will be called on every attempted withdraw, all deposits will be permanently unrecoverable.

Impact:
All deposits will be permanently unrecoverable

Recommended mitigation:
Check if reward is stash AURA and send regular AURA instead similar to what is done in AuraSpell.
Vulnerability: "Votes" balance can be increased indefinitely in multiple contracts
Severity: high

Description:
The "voting power" can be easily manipulated in the following contracts:\\n`ContinuousVestingMerkle`\\n`PriceTierVestingMerkle`\\n`PriceTierVestingSale_2_0`\\n`TrancheVestingMerkle`\\n`CrosschainMerkleDistributor`\\n`CrosschainContinuousVestingMerkle`\\n`CrosschainTrancheVestingMerkle`\\nAll the contracts inheriting from the contracts listed above\\nThis is caused by the public `initializeDistributionRecord()` function that can be recalled multiple times without any kind of access control:\\n```\\n  function initializeDistributionRecord(\\n    uint32 _domain, // the domain of the beneficiary\\n    address _beneficiary, // the address that will receive tokens\\n    uint256 _amount, // the total claimable by this beneficiary\\n    bytes32[] calldata merkleProof\\n  ) external validMerkleProof(_getLeaf(_beneficiary, _amount, _domain), merkleProof) {\\n    _initializeDistributionRecord(_beneficiary, _amount);\\n  }\\n```\\n\\nThe `AdvancedDistributor` abstract contract which inherits from the `ERC20Votes`, `ERC20Permit` and `ERC20` contracts, distributes tokens to beneficiaries with voting-while-vesting and administrative controls. Basically, before the tokens are vested/claimed by a certain group of users, these users can use these `ERC20` tokens to vote. These tokens are minted through the `_initializeDistributionRecord()` function:\\n```\\n  function _initializeDistributionRecord(\\n    address beneficiary,\\n    uint256 totalAmount\\n  ) internal virtual override {\\n    super._initializeDistributionRecord(beneficiary, totalAmount);\\n\\n    // add voting power through ERC20Votes extension\\n    _mint(beneficiary, tokensToVotes(totalAmount));\\n  }\\n```\\n\\nAs mentioned in the Tokensoft Discord channel these ERC20 tokens minted are used to track an address's unvested token balance, so that other projects can utilize 'voting while vesting'.\\nA user can simply call as many times as he wishes the `initializeDistributionRecord()` function with a valid merkle proof. With each call, the `totalAmount` of tokens will be minted. Then, the user simply can call `delegate()` and delegate those votes to himself, "recording" the inflated voting power.

Impact:
The issue totally breaks the 'voting while vesting' design. Any DAO/project using these contracts to determine their voting power could be easily manipulated/exploited.

Recommended mitigation:
Only allow users to call once the `initializeDistributionRecord()` function. Consider using a mapping to store if the function was called previously or not. Keep also in mind that fully vested and claimed users should not be able to call this function and if they do, the total amount of tokens that should be minted should be 0 or proportional/related to the amount of tokens that they have already claimed.
Vulnerability: `SafeERC20.safeApprove` reverts for changing existing approvals
Severity: medium

Description:
`SafeERC20.safeApprove` reverts when a non-zero approval is changed to a non-zero approval. The `CrosschainDistributor._setTotal` function tries to change an existing approval to a non-zero value which will revert.\\nThe safeApprove function has explicit warning:\\n```\\n        // safeApprove should only be called when setting an initial allowance,\\n        // or when resetting it to zero. To increase and decrease it, use\\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n```\\n\\nBut still the `_setTotal` use it to change approval amount:\\n```\\n  function _allowConnext(uint256 amount) internal {\\n    token.safeApprove(address(connext), amount);\\n  }\\n\\n  /** Reset Connext allowance when total is updated */\\n  function _setTotal(uint256 _total) internal virtual override onlyOwner {\\n    super._setTotal(_total);\\n    _allowConnext(total - claimed);\\n  }\\n```\\n

Impact:
Due to this bug all calls to `setTotal` function of `CrosschainContinuousVestingMerkle` and `CrosschainTrancheVestingMerkle` will get reverted.\\nTokensoft airdrop protocol is meant to be used by other protocols and the ability to change `total` parameter is an intended offering. This feature will be important for those external protocols due to the different nature & requirement of every airdrop. But this feature will not be usable by airdrop owners due to the incorrect code implementation.

Recommended mitigation:
Consider using 'safeIncreaseAllowance' and 'safeDecreaseAllowance' instead of `safeApprove` in `_setTotal`.
Vulnerability: CrosschainDistributor: Not paying relayer fee when calling xcall to claim tokens to other domains
Severity: medium

Description:
CrosschainDistributor is not paying relayer fee when calling xcall to claim tokens to other domains. The transaction will not be relayed on target chain to finnalize the claim. User will not receive the claimed tokens unless they bump the transaction fee themself.\\nIn `_settleClaim`, the CrosschainDistributor is using xcall to claim tokens to another domain. But relayer fee is not payed.\\n```\\n      id = connext.xcall(            // <------ relayer fee should be payed here\\n        _recipientDomain, // destination domain\\n        _recipient, // to\\n        address(token), // asset\\n        _recipient, // delegate, only required for self-execution + slippage\\n        _amount, // amount\\n        0, // slippage -- assumes no pools on connext\\n        bytes('') // calldata\\n      );\\n```\\n\\nWithout the relayer fee, the transaction will not be relayed. The user will need to bump the relayer fee to finnally settle the claim by following the instructions here in the connext doc.

Impact:
User will not receive their claimed tokens on target chain.

Recommended mitigation:
Help user bump the transaction fee in Satellite.
Vulnerability: price is calculated wrongly in BoundedStepwiseExponentialPriceAdapter
Severity: medium

Description:
The BoundedStepwiseExponentialPriceAdapter contract is trying to implement price change as `scalingFactor * (e^x - 1)` but the code implements `scalingFactor * e^x - 1`. Since there are no brackets, multiplication would be executed before subtraction. And this has been confirmed with one of the team members.\\nThe getPrice code has been simplified as the following when boundary/edge cases are ignored\\n```\\n(\\n    uint256 initialPrice,\\n    uint256 scalingFactor,\\n    uint256 timeCoefficient,\\n    uint256 bucketSize,\\n    bool isDecreasing,\\n    uint256 maxPrice,\\n    uint256 minPrice\\n) = getDecodedData(_priceAdapterConfigData);\\n\\nuint256 timeBucket = _timeElapsed / bucketSize;\\n\\nint256 expArgument = int256(timeCoefficient * timeBucket);\\n\\nuint256 expExpression = uint256(FixedPointMathLib.expWad(expArgument));\\n\\nuint256 priceChange = scalingFactor * expExpression - WAD;\\n```\\n\\nWhen timeBucket is 0, we want priceChange to be 0, so that the returned price would be the initial price. Since `e^0 = 1`, we need to subtract 1 (in WAD) from the `expExpression`.\\nHowever, with the incorrect implementation, the returned price would be different than real price by a value equal to `scalingFactor - 1`. The image below shows the difference between the right and wrong formula when initialPrice is 100 and scalingFactor is 11. The right formula starts at 100 while the wrong one starts at 110=100+11-1\\n

Impact:
Incorrect price is returned from BoundedStepwiseExponentialPriceAdapter and that will have devastating effects on rebalance.

Recommended mitigation:
Change the following line\\n```\\n- uint256 priceChange = scalingFactor * expExpression - WAD;\\n+ uint256 priceChange = scalingFactor * (expExpression - WAD);\\n```\\n
Vulnerability: Full inventory asset purchases can be DOS'd via frontrunning
Severity: medium

Description:
Users who attempt to swap the entire component value can be frontrun with a very small bid making their transaction revert\\nAuctionRebalanceModuleV1.sol#L795-L796\\n```\\n    // Ensure that the component quantity in the bid does not exceed the available auction quantity.\\n    require(_componentQuantity <= bidInfo.auctionQuantity, "Bid size exceeds auction quantity");\\n```\\n\\nWhen creating a bid, it enforces the above requirement. This prevents users from buying more than they should but it is also a source of an easy DOS attack. Assume a user is trying to buy the entire balance of a component, a malicious user can frontrun them buying only a tiny amount. Since they requested the entire balance, the call with fail. This is a useful technique if an attacker wants to DOS other buyers to pass the time and get a better price from the dutch auction.

Impact:
Malicious user can DOS legitimate users attempting to purchase the entire amount of component

Recommended mitigation:
Allow users to specify type(uint256.max) to swap the entire available balance
Vulnerability: All fund from Teller contract can be drained because a malicious receiver can call reclaim repeatedly
Severity: high

Description:
All fund from Teller contract can be drained because a malicious receiver can call reclaim repeatedly\\nWhen mint an option token, the user is required to transfer the payout token for a call option or quote token for a put option\\nif after the expiration, the receiver can call reclaim to claim the payout token if the option type is call or claim the quote token if the option type is put\\nhowever, the root cause is when reclaim the token, the corresponding option is not burnt (code)\\n```\\n       // Revert if caller is not receiver\\n        if (msg.sender != receiver) revert Teller_NotAuthorized();\\n\\n        // Transfer remaining collateral to receiver\\n        uint256 amount = optionToken.totalSupply();\\n        if (call) {\\n            payoutToken.safeTransfer(receiver, amount);\\n        } else {\\n            // Calculate amount of quote tokens equivalent to amount at strike price\\n            uint256 quoteAmount = amount.mulDiv(strikePrice, 10 ** payoutToken.decimals());\\n            quoteToken.safeTransfer(receiver, quoteAmount);\\n        }\\n```\\n\\nthe Teller contract is likely to hold fund from multiple option token\\na malicious actor can create call Teller#deploy and set a receiver address that can control by himself\\nand then wait for the option expiry and repeated call reclaim to steal the fund from the Teller contract

Impact:
All fund from Teller contract can be drained because a malicious receiver can call reclaim repeatedly

Recommended mitigation:
Burn the corresponding option burn when reclaim the fund
Vulnerability: Blocklisted address can be used to lock the option token minter's fund
Severity: medium

Description:
Blocklisted address can be used to lock the option token minter's fund\\nWhen deploy a token via the teller contract, the contract validate that receiver address is not address(0)\\nHowever, a malicious option token creator can save a seemingly favorable strike price and pick a blocklisted address and set the blocklisted address as receiver\\nSome tokens (e.g. USDC, USDT) have a contract level admin controlled address blocklist. If an address is blocked, then transfers to and from that address are forbidden.\\nMalicious or compromised token owners can trap funds in a contract by adding the contract address to the blocklist. This could potentially be the result of regulatory action against the contract itself, against a single user of the contract (e.g. a Uniswap LP), or could also be a part of an extortion attempt against users of the blocked contract.\\nthen user would see the favorable strike price and mint the option token using payout token for call option or use quote token for put option\\nHowever, they can never exercise their option because the transaction would revert when transferring asset to the recevier for call option and transferring asset to the receiver for put option when exercise the option.\\n```\\n\\n```\\n\\nthe usre's fund that used to mint the option are locked

Impact:
Blocklisted receiver address can be used to lock the option token minter's fund

Recommended mitigation:
Valid that receiver is not blacklisted when create and deploy the option token or add an expiry check, if after the expiry the receiver does not reclaim the fund, allows the option minter to burn their token in exchange for their fund
Vulnerability: Loss of option token from Teller and reward from OTLM if L2 sequencer goes down
Severity: medium

Description:
Loss of option token from Teller and reward from OTLM if L2 sequencer goes down\\nIn the current implementation, if the option token expires, the user is not able to exerise the option at strike price\\n```\\n    // Validate that option token is not expired\\n        if (uint48(block.timestamp) >= expiry) revert Teller_OptionExpired(expiry);\\n```\\n\\nif the option token expires, the user lose rewards from OTLM as well when claim the reward\\n```\\n    function _claimRewards() internal returns (uint256) {\\n        // Claims all outstanding rewards for the user across epochs\\n        // If there are unclaimed rewards from epochs where the option token has expired, the rewards are lost\\n\\n        // Get the last epoch claimed by the user\\n        uint48 userLastEpoch = lastEpochClaimed[msg.sender];\\n```\\n\\nand\\n```\\n    // If the option token has expired, then the rewards are zero\\n        if (uint256(optionToken.expiry()) < block.timestamp) return 0;\\n```\\n\\nAnd in the onchain context, the protocol intends to deploy the contract in arbitrum and optimsim\\n```\\nQ: On what chains are the smart contracts going to be deployed?\\nMainnet, Arbitrum, Optimism\\n```\\n\\nHowever, If Arbitrum and optimism layer 2 network, the sequencer is in charge of process the transaction\\nFor example, the recent optimism bedrock upgrade cause the sequencer not able to process transaction for a hew hours\\nBedrock Upgrade According to the official announcement, the upgrade will require 2-4 hours of downtime for OP Mainnet, during which there will be downtime at the chain and infrastructure level while the old sequencer is spun down and the Bedrock sequencer starts up.\\nTransactions, deposits, and withdrawals will also remain unavailable for the duration, and the chain will not be progressing. While the read access across most OP Mainnet nodes will stay online, users may encounter a slight decrease in performance during the migration process.\\nIn Arbitrum\\nArbitrum Goes Down Citing Sequencer Problems Layer 2 Arbitrum suffers 10 hour outage.\\nand\\nEthereum layer-2 (L2) scaling solution Arbitrum stopped processing transactions on June 7 because its sequencer faced a bug in the batch poster. The incident only lasted for an hour.\\nIf the option expires during the sequencer down time, the user basically have worthless option token because they cannot exercise the option at strike price\\nthe user would lose his reward as option token from OTLM.sol, which defeats the purpose of use OTLM to incentive user to provide liquidity

Impact:
Loss of option token from Teller and reward from OTLM if L2 sequencer goes down

Recommended mitigation:
chainlink has a sequencer up feed\\nconsider integrate the up time feed and give user extra time to exercise token and claim option token reward if the sequencer goes down