msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: `onTokenTransfer` does not validate if the call is from the DATA token contract
Severity: medium

Description:
`SponsorshipFactory::onTokenTransfer` and `OperatorFactory::onTokenTransfer` are used to handle the token transfer and contract deployment in a single transaction. But there is no validation that the call is from the DATA token contract and anyone can call these functions.\\nThe impact is low for `Sponsorship` deployment, but for `Operator` deployment, `ClonesUpgradeable.cloneDeterministic` is used with a salt based on the operator token name and the operator address. An attacker can abuse this to cause DoS for deployment.\\nWe see that this validation is implemented correctly in other contracts like `Operator`.\\n```\\n       if (msg.sender != address(token)) {\\n           revert AccessDeniedDATATokenOnly();\\n       }\\n```\\n

Impact:
Attackers can prevent the deployment of `Operator` contracts.

Recommended mitigation:
Add a validation to ensure the caller is the actual DATA contract.
Vulnerability: Use safe transfer for ERC20 tokens
Severity: medium

Description:
The protocol intends to support all ERC20 tokens but the implementation uses the original transfer functions. Some tokens (like USDT) do not implement the EIP20 standard correctly and their transfer/transferFrom function return void instead of a success boolean. Calling these functions with the correct EIP20 function signatures will revert.\\n```\\nTransferUtils.sol\\n    function _transferERC20(address token, address to, uint256 amount) internal {\\n        IERC20 erc20 = IERC20(token);\\n        require(erc20 != IERC20(address(0)), "Token Address is not an ERC20");\\n        uint256 initialBalance = erc20.balanceOf(to);\\n        require(erc20.transfer(to, amount), "ERC20 Transfer failed");//@audit-issue will revert for USDT\\n        uint256 balance = erc20.balanceOf(to);\\n        require(balance >= (initialBalance + amount), "ERC20 Balance check failed");\\n    }\\n```\\n

Impact:
Tokens that do not correctly implement the EIP20 like USDT, will be unusable in the protocol as they revert the transaction because of the missing return value.

Recommended mitigation:
We recommend using OpenZeppelin's SafeERC20 versions with the safeTransfer and safeTransferFrom functions that handle the return value check as well as non-standard-compliant tokens.
Vulnerability: Fee-on-transfer tokens are not supported
Severity: medium

Description:
The protocol intends to support all ERC20 tokens but does not support fee-on-transfer tokens. The protocol utilizes the functions `TransferUtils::_transferERC20()` and `TransferUtils::_transferFromERC20()` to transfer ERC20 tokens.\\n```\\nTransferUtils.sol\\n    function _transferERC20(address token, address to, uint256 amount) internal {\\n        IERC20 erc20 = IERC20(token);\\n        require(erc20 != IERC20(address(0)), "Token Address is not an ERC20");\\n        uint256 initialBalance = erc20.balanceOf(to);\\n        require(erc20.transfer(to, amount), "ERC20 Transfer failed");\\n        uint256 balance = erc20.balanceOf(to);\\n        require(balance >= (initialBalance + amount), "ERC20 Balance check failed");//@audit-issue reverts for fee on transfer token\\n    }\\n```\\n\\nThe implementation verifies that the transfer was successful by checking that the balance of the recipient is greater than or equal to the initial balance plus the amount transferred. This check will fail for fee-on-transfer tokens because the actual received amount will be less than the input amount. (Read here about fee-on-transfer tokens)\\nAlthough there are very few fee-on-transfer tokens, the protocol can't say it supports all ERC20 tokens if it doesn't support these weird ERC20 tokens.

Impact:
Fee-on-transfer tokens can not be used for the protocol. Because of the rarity of these tokens, we evaluate this finding as a Medium risk.

Recommended mitigation:
The transfer utility functions can be updated to return the actually received amount. Or clearly document that only standard ERC20 tokens are supported.
Vulnerability: Centralization risk
Severity: medium

Description:
The protocol has an owner with privileged rights to perform admin tasks that can affect users. Especially, the owner can change the fee settings and reward handler address.\\nValidation is missing for admin fee setter functions.\\n```\\nFeeData.sol\\n    function setFeeValue(uint256 feeValue) external onlyOwner {\\n        require(feeValue < _feeDenominator, "Fee percentage must be less than 1");\\n        _feeValue = feeValue;\\n    }\\n\\n    function setFixedFee(uint256 fixedFee) external onlyOwner {//@audit-issue validate min/max\\n        _fixedFee = fixedFee;\\n    }\\n```\\n\\nImportant changes initiated by admin should be logged via events.\\n```\\nFile: helpers/FeeData.sol\\n\\n    function setFeeValue(uint256 feeValue) external onlyOwner {\\n\\n    function setMaxHops(uint256 maxHops) external onlyOwner {\\n\\n    function setMaxSwaps(uint256 maxSwaps) external onlyOwner {\\n\\n    function setFixedFee(uint256 fixedFee) external onlyOwner {\\n\\n    function setFeeToken(address feeTokenAddress) public onlyOwner {\\n\\n    function setFeeTokens(address[] memory feeTokenAddresses) public onlyOwner {\\n\\n    function clearFeeTokens() public onlyOwner {\\n```\\n\\n```\\nFile: helpers/TransferHelper.sol\\n\\n    function setRewardHandler(address rewardAddress) external onlyOwner {\\n\\n    function setRewardsActive(bool _rewardsActive) external onlyOwner {\\n```\\n

Impact:
While the protocol owner is regarded as a trusted party, the owner can change the fee settings and reward handler address without any validation or logging. This can lead to unexpected results and users can be affected.

Recommended mitigation:
Specify the owner's privileges and responsibilities in the documentation.\\nAdd constant state variables that can be used as the minimum and maximum values for the fee settings.\\nAdd proper validation for the admin functions.\\nLog the changes in the important state variables via events.
Vulnerability: Spender can front-run calls to modify token allowances, resulting in DoS and/or spending more than was intended
Severity: low

Description:
When updating the allowance for a spender that is less than the value currently set, a well-known race condition allows the spender to spend more than the caller intended by front-running the transaction that performs this update. Due to the nature of the `ERC20::approve` implementation and other variants used within the Beanstalk system, which update the mapping in storage corresponding to the given allowance, the spender can spend both the existing allowance plus any 'additional' allowance set by the in-flight transaction.\\nFor example, consider the scenario:\\nAlice approves Bob 100 tokens.\\nAlice later decides to decrease this to 50.\\nBob sees this transaction in the mempool and front-runs, spending his 100 token allowance.\\nAlice's transaction executes, and Bob's allowance is updated to 50.\\nBob can now spend an additional 50 tokens, resulting in a total of 150 rather than the maximum of 50 as intended by Alice.\\nSpecific functions named `decreaseTokenAllowance`, intended to decrease approvals for a token spender, have been introduced to both the `TokenFacet` and the `ApprovalFacet`. `PodTransfer::decrementAllowancePods` similarly exists for the Pod Marketplace.\\nThe issue, however, with these functions is that they are still susceptible to front-running in the sense that a malicious spender could force their execution to revert, violating the intention of the caller to decrease their allowance as they continue to spend that which is currently set. Rather than simply setting the allowance to zero if the caller passes an amount to subtract that is larger than the current allowance, these functions halt execution and revert. This is due to the following line of shared logic:\\n```\\nrequire(\\n    currentAllowance >= subtractedValue,\\n    "Silo: decreased allowance below zero"\\n);\\n```\\n\\nConsider the following scenario:\\nAlice approves Bob 100 tokens.\\nAlice later decides to decrease this to 50.\\nBob sees this transaction in the mempool and front-runs, spending 60 of his 100 token allowance.\\nAlice's transaction executes, but reverts given Bob's allowance is now 40.\\nBob can now spend the remaining 40 tokens, resulting in a total of 100 rather than the decreased amount of 50 as intended by Alice.\\nOf course, in this scenario, Bob could have just as easily front-run Alice's transaction and spent his entire existing allowance; however, the fact that he is able to perform a denial-of-service attack results in a degraded user experience. Similar to setting maximum approvals, these functions should handle maximum approval revocations to mitigate against this issue.

Impact:
Requiring that the intended subtracted allowance does not exceed the current allowance results in a degraded user experience and, more significantly, their loss of funds due to a different route to the same approval front-running attack vector.

Recommended mitigation:
Set the allowance to zero if the intended subtracted value exceeds the current allowance.\\n\\clearpage
Vulnerability: Non-standard ERC20 tokens are not supported
Severity: medium

Description:
The protocol implemented a function `deposit()` to allow users to deposit.\\n```\\nDepositVault.sol\\n    function deposit(uint256 amount, address tokenAddress) public payable {\\n        require(amount > 0 || msg.value > 0, "Deposit amount must be greater than 0");\\n        if(msg.value > 0) {\\n            require(tokenAddress == address(0), "Token address must be 0x0 for ETH deposits");\\n            uint256 depositIndex = deposits.length;\\n            deposits.push(Deposit(payable(msg.sender), msg.value, tokenAddress));\\n            emit DepositMade(msg.sender, depositIndex, msg.value, tokenAddress);\\n        } else {\\n            require(tokenAddress != address(0), "Token address must not be 0x0 for token deposits");\\n            IERC20 token = IERC20(tokenAddress);\\n            token.safeTransferFrom(msg.sender, address(this), amount);\\n            uint256 depositIndex = deposits.length;\\n            deposits.push(Deposit(payable(msg.sender), amount, tokenAddress));//@audit-issue fee-on-transfer, rebalancing tokens will cause problems\\n            emit DepositMade(msg.sender, depositIndex, amount, tokenAddress);\\n        }\\n    }\\n```\\n\\nLooking at the line L49, we can see that the protocol assumes `amount` of tokens were transferred. But this does not hold true for some non-standard ERC20 tokens like fee-on-transfer tokens or rebalancing tokens. (Refer to here about the non-standard weird ERC20 tokens)\\nFor example, if token incurs fee on transfer, the actually transferred `amount` will be less than the provided parameter `amount` and the `deposits` will have a wrong state value. Because the current implementation only allows full withdrawal, this means the tokens will be locked in the contract permanently.

Impact:
If non-standard ERC20 tokens are used, the tokens could be locked in the contract permanently.

Recommended mitigation:
We recommend adding another field in the `Deposit` structure, say `balance`\\nWe recommend allow users to withdraw partially and decrease the `balance` field appropriately for successful withdrawals. If these changes are going to be made, we note that there are other parts that need changes. For example, the withdraw function would need to be updated so that it does not require the withdrawal amount is same to the original deposit amount.
Vulnerability: The deposit function is not following CEI pattern
Severity: low

Description:
The protocol implemented a function `deposit()` to allow users to deposit.\\n```\\nDepositVault.sol\\n    function deposit(uint256 amount, address tokenAddress) public payable {\\n        require(amount > 0 || msg.value > 0, "Deposit amount must be greater than 0");\\n        if(msg.value > 0) {\\n            require(tokenAddress == address(0), "Token address must be 0x0 for ETH deposits");\\n            uint256 depositIndex = deposits.length;\\n            deposits.push(Deposit(payable(msg.sender), msg.value, tokenAddress));\\n            emit DepositMade(msg.sender, depositIndex, msg.value, tokenAddress);\\n        } else {\\n            require(tokenAddress != address(0), "Token address must not be 0x0 for token deposits");\\n            IERC20 token = IERC20(tokenAddress);\\n            token.safeTransferFrom(msg.sender, address(this), amount);//@audit-issue against CEI pattern\\n            uint256 depositIndex = deposits.length;\\n            deposits.push(Deposit(payable(msg.sender), amount, tokenAddress));\\n            emit DepositMade(msg.sender, depositIndex, amount, tokenAddress);\\n        }\\n    }\\n```\\n\\nLooking at the line L47, we can see that the token transfer happens before updating the accounting state of the protocol against the CEI pattern. Because the protocol intends to support all ERC20 tokens, the tokens with hooks (e.g. ERC777) can be exploited for reentrancy. Although we can not verify an exploit that causes explicit loss due to this, it is still highly recommended to follow CEI pattern to prevent possible reentrancy attack.

Impact:

Recommended mitigation:
Handle token transfers after updating the `deposits` state.
Vulnerability: Nonstandard usage of nonce
Severity: low

Description:
The protocol implemented two withdraw functions `withdrawDeposit()` and `withdraw()`. While the function `withdrawDeposit()` is designed to be used by the depositor themselves, the function `withdraw()` is designed to be used by anyone who has a signature from the depositor. The function `withdraw()` has a parameter `nonce` but the usage of this param is not aligned with the general meaning of `nonce`.\\n```\\nDepositVault.sol\\n    function withdraw(uint256 amount, uint256 nonce, bytes memory signature, address payable recipient) public {\\n        require(nonce < deposits.length, "Invalid deposit index");\\n        Deposit storage depositToWithdraw = deposits[nonce];//@audit-info non aligned with common understanding of nonce\\n        bytes32 withdrawalHash = getWithdrawalHash(Withdrawal(amount, nonce));\\n        address signer = withdrawalHash.recover(signature);\\n        require(signer == depositToWithdraw.depositor, "Invalid signature");\\n        require(!usedWithdrawalHashes[withdrawalHash], "Withdrawal has already been executed");\\n        require(amount == depositToWithdraw.amount, "Withdrawal amount must match deposit amount");\\n        usedWithdrawalHashes[withdrawalHash] = true;\\n        depositToWithdraw.amount = 0;\\n        if(depositToWithdraw.tokenAddress == address(0)){\\n            recipient.transfer(amount);\\n        } else {\\n            IERC20 token = IERC20(depositToWithdraw.tokenAddress);\\n            token.safeTransfer(recipient, amount);\\n        }\\n        emit WithdrawalMade(recipient, amount);\\n    }\\n```\\n\\nIn common usage, `nonce` is used to track the latest transaction from the EOA and generally it is increased on the user's transaction. It can be effectively used to invalidate the previous signature by the signer. But looking at the current implementation, the parameter `nonce` is merely used as an index to refer the `deposit` at a specific index.\\nThis is a bad naming and can confuse the users.

Impact:

Recommended mitigation:
If the protocol intended to provide a kind of invalidation mechanism using the nonce, there should be a separate mapping that stores the nonce for each user. The current nonce can be used to generate a signature and a depositor should be able to increase the nonce to invalidate the previous signatures. Also the nonce would need to be increased on every successful call to `withdraw()` to prevent replay attack. Please note that with this remediation, the mapping `usedWithdrawalHashes` can be removed completely because the hash will be always decided using the latest nonce and the nonce will be invalidated automatically (because it increases on successful call).\\nIf this is not what the protocol intended, the parameter nonce can be renamed to `depositIndex` as implemented in the function `withdrawDeposit()`.
Vulnerability: Unnecessary parameter amount in withdraw function
Severity: low

Description:
The function `withdraw()` has a parameter `amount` but we don't understand the necessity of this parameter. At line L67, the `amount` is required to be the same to the whole deposit `amount`. This means the user does not have a flexibility to choose the withdraw `amount`, after all it means the parameter was not necessary at all.\\n```\\nDepositVault.sol\\n    function withdraw(uint256 amount, uint256 nonce, bytes memory signature, address payable recipient) public {\\n        require(nonce < deposits.length, "Invalid deposit index");\\n        Deposit storage depositToWithdraw = deposits[nonce];\\n        bytes32 withdrawalHash = getWithdrawalHash(Withdrawal(amount, nonce));\\n        address signer = withdrawalHash.recover(signature);\\n        require(signer == depositToWithdraw.depositor, "Invalid signature");\\n        require(!usedWithdrawalHashes[withdrawalHash], "Withdrawal has already been executed");\\n        require(amount == depositToWithdraw.amount, "Withdrawal amount must match deposit amount");//@audit-info only full withdrawal is allowed\\n        usedWithdrawalHashes[withdrawalHash] = true;\\n        depositToWithdraw.amount = 0;\\n        if(depositToWithdraw.tokenAddress == address(0)){\\n            recipient.transfer(amount);\\n        } else {\\n            IERC20 token = IERC20(depositToWithdraw.tokenAddress);\\n            token.safeTransfer(recipient, amount);\\n        }\\n        emit WithdrawalMade(recipient, amount);\\n    }\\n```\\n

Impact:

Recommended mitigation:
If the protocol intends to only allow full withdrawal, this parameter can be removed completely (that will help save gas as well). Unnecessary parameters increase the complexity of the function and more error prone.
Vulnerability: Functions not used internally could be marked external
Severity: low

Description:
Using proper visibility modifiers is a good practice to prevent unintended access to functions. Furthermore, marking functions as `external` instead of `public` can save gas.\\n```\\nFile: DepositVault.sol\\n\\n    function deposit(uint256 amount, address tokenAddress) public payable\\n\\n    function withdraw(uint256 amount, uint256 nonce, bytes memory signature, address payable recipient) public\\n\\n    function withdrawDeposit(uint256 depositIndex) public\\n```\\n

Impact:

Recommended mitigation:
Consider change the visibility modifier to `external` for the functions that are not used internally.