msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: MozToken will have a much larger fixed supply than intended.
Severity: medium

Description:
MozToken is planned to be deployed on all supported chains. Its total supply will be 1B. However, its constructor will mint 1B tokens on each deployment.\\n```\\n        constructor( address _layerZeroEndpoint, uint8 _sharedDecimals\\n            ) OFTV2("Mozaic Token", "MOZ", _sharedDecimals, _layerZeroEndpoint) {\\n            _mint(msg.sender, 1000000000 * 10 ** _sharedDecimals);\\n                isAdmin[msg.sender] = true;\\n            }\\n```\\n

Impact:

Recommended mitigation:
Pass the minted supply as a parameter. Only on the main chain, mint 1B tokens.
Vulnerability: Theoretical reentrancy attack when TYPE_MINT_BURN proposals are executed
Severity: low

Description:
The senate can pass a proposal to mint or burn tokens.\\n```\\n       if(proposals[_proposalId].actionType == TYPE_MINT_BURN) {\\n           (address _token, address _to, uint256 _amount, bool _flag) = \\n               abi.decode(proposals[_proposalId].payload, (address, address, uint256, bool));\\n       if(_flag) {\\n           IXMozToken(_token).mint(_amount, _to);\\n       } else {\\n                    IXMozToken(_token).burn(_amount, _to);\\n       }\\n       proposals[_proposalId].executed = true;\\n       }\\n```\\n\\nNote that the proposal is only marked as executed at the end of execution, but execution is checked at the start of the function.\\n```\\n        function execute(uint256 _proposalId) public onlyCouncil {\\n                 require(proposals[_proposalId].executed == false, "Error: \\n            Proposal already executed.");\\n        require(proposals[_proposalId].confirmation >= threshold, "Error: Not enough confirmations.");\\n```\\n\\nInteraction with tokens should generally be assumed to grant arbitrary call execution to users. If the mint or `burn()` calls call `execute()` again, the proposal will be executed twice, resulting in double the amount minted or burned. Specifically for XMoz, it is not anticipated to yield execution to the to address, so the threat remains theoretical.

Impact:

Recommended mitigation:
Follow the Check-Effects-Interactions design pattern, mark the function as executed at the start.
Vulnerability: XMozToken permits transfers from non-whitelisted addresses
Severity: low

Description:
The XMozToken is documented to forbid transfers except from whitelisted addresses or mints.\\n```\\n        /**\\n        * @dev Hook override to forbid transfers except from whitelisted \\n        addresses and minting\\n        */\\n        function _beforeTokenTransfer(address from, address to, uint256 \\n        /*amount*/) internal view override {\\n                require(from == address(0) || _transferWhitelist.contains(from) \\n                  || _transferWhitelist.contains(to), "transfer: not allowed");\\n                 }\\n```\\n\\nHowever, as can be seen, non-whitelisted users can still transfer tokens, so long as it is to whitelisted destinations.

Impact:

Recommended mitigation:
Remove the additional check in `_beforeTokenTransfer()`, or update the documentation accordingly.
Vulnerability: XMozToken cannot be added to its own whitelist
Severity: low

Description:
By design, XMozToken should always be in the whitelist. However, `updateTransferWhitelist()` implementation forbids both removal and insertion of XMozToken to the whitelist.\\n```\\n            function updateTransferWhitelist(address account, bool add) external  onlyMultiSigAdmin {\\n                    require(account != address(this), "updateTransferWhitelist: \\n                          Cannot remove xMoz from whitelist");\\n                    if(add) _transferWhitelist.add(account);\\n                 else _transferWhitelist.remove(account);\\n            emit SetTransferWhitelist(account, add);\\n            }\\n```\\n

Impact:

Recommended mitigation:
Move the require statement into the else clause.
Vulnerability: User fee token balance can be drained in a single operation by a malicious bot
Severity: high

Description:
In `_buildFeeExecutable()`, BrahRouter calculates the total fee charged to the wallet. It uses tx. gas price to get the gas price specified by the bot.\\n```\\n  if (feeToken == ETH) \\n   {uint256 totalFee = (gasUsed + GAS_OVERHEAD_NATIVE) * tx.gasprice;\\n     totalFee = _applyMultiplier(totalFee);\\n       return (totalFee, recipient, TokenTransfer._nativeTransferExec(recipient, totalFee));\\n            } else {uint256 totalFee = (gasUsed + GAS_OVERHEAD_ERC20) * tx.gasprice;\\n      // Convert fee amount value in fee tokenuint256 feeToCollect =PriceFeedManager(_addressProvider.priceFeedManager()).getTokenXPriceInY(totalFee, ETH, feeToken);\\n  feeToCollect = _applyMultiplier(feeToCollect);\\n return (feeToCollect, recipient, TokenTransfer._erc20TransferExec(feeToken, recipient, feeToCollect));}\\n```\\n

Impact:

Recommended mitigation:
Use a gas oracle or a capped priority fee to ensure an inflated gas price down not harm the user.
Vulnerability: Users can drain Gelato deposit at little cost
Severity: high

Description:
In Console automation, fees are collected via the `claimExecutionFees()` modifier:\\n```\\n   modifier claimExecutionFees(address _wallet) {\\n     uint256 startGas = gasleft();\\n  _;\\n if (feeMultiplier > 0) {\\n    address feeToken = FeePayer._feeToken(_wallet);\\n      uint256 gasUsed = startGas -gasleft();\\n       (uint256 feeAmount, address recipient, Types.Executable memory feeTransferTxn)=FeePayer._buildFeeExecutable\\n         (gasUsed, feeToken);\\n emit FeeClaimed(_wallet, feeToken, feeAmount);\\n   if (feeToken != ETH) {uint256 initialBalance = IERC20(feeToken).balanceOf(recipient);_\\n       executeSafeERC20Transfer(_wallet, feeTransferTxn);\\n             if (IERC20(feeToken).balanceOf(recipient) -initialBalance < feeAmount){\\n        revert UnsuccessfulFeeTransfer(_wallet, feeToken);}\\n     } else {\\n    uint256 initialBalance = recipient.balance;\\n  Executor._executeOnWallet(_wallet, feeTransferTxn);\\n if (recipient.balance -initialBalance < feeAmount) {\\n      revert UnsuccessfulFeeTransfer(_wallet, feeToken);\\n                }\\n            }\\n        }\\n    }\\n```\\n

Impact:

Recommended mitigation:
When calculating fees in buildFeeExecutable(), there are assumptions about the gas cost of an ERC20 transfer and a native transfer.\\n```\\n    // Keeper network overhead -150k\\n      uint256 internal constant GAS_OVERHEAD_NATIVE = 150_000 + 40_000;\\n        uint256 internal constant GAS_OVERHEAD_ERC20 = 150_000 + 90_000;\\n```\\n\\nA good fix would be to check the actual gas usage and require it to be under the hard cap.Team responseAdded a gas check for this attack.Mitigation reviewApplied fix has been applied.
Vulnerability: Attackers can drain users over time by donating negligible ERC20 amount
Severity: high

Description:
In the Console automation model, a strategy shall keep executing until its trigger check fails. For DCA strategies, the swapping trigger is defined as:\\n```\\n      function canInitSwap(address subAccount, address inputToken, uint256 interval, uint256 lastSwap)\\n          external view returns (bool)\\n        {\\n      if (hasZeroBalance(subAccount, inputToken)) \\n          { return false;\\n        }\\n      return ((lastSwap + interval) < block.timestamp);\\n      }\\n```\\n

Impact:

Recommended mitigation:
Define a DUST_AMOUNT, below that amount exit is allowed, while above that amount swap execution is allowed. User should only stand to gain from another party donating ERC20 tokens to their account.
Vulnerability: When FeePayer is subsidizing, users can steal gas
Severity: medium

Description:
```\\nThe feeMultiplier enables the admin to subsidize or upcharge for the automation service.\\n/**\\n⦁ @notice feeMultiplier represents the total fee to be charged on the transaction\\n⦁ Is set to 100% by default\\n⦁ @dev In case feeMultiplier is less than BASE_BPS, fees charged will be less than 100%,\\n⦁ subsidizing the transaction\\n⦁ In case feeMultiplier is greater than BASE_BPS, fees charged will be greater than 100%,\\n⦁ charging the user for the transaction\\n*/ \\n      uint16 public feeMultiplier = 10_000;\\n        // The normal fee is calculated and then processed by the multiplier.\\n  if (feeToken == ETH) {\\n         uint256 totalFee = (gasUsed + GAS_OVERHEAD_NATIVE) * tx.gasprice; \\n      totalFee = _applyMultiplier(totalFee);\\n    return (totalFee, recipient, TokenTransfer._nativeTransferExec(recipient, totalFee));\\n  } else {\\n```\\n

Impact:

Recommended mitigation:
The root cause is that the gasUsed amount is subsidized as well as GAS_OVERHEAD_NATIVE, which is the gas reserved for the delivery from Gelato executors. By subsidizing only the Gelato gas portion, users will not gain from gas minting attacks, while the intention of improving user experience is maintained.
Vulnerability: Strategy actions could be executed out of order due to lack of reentrancy guard
Severity: medium

Description:
The Execute module performs automation of the fetched Executable array on wallet subaccounts.\\n```\\n    function _executeAutomation( address _wallet, address _subAccount, address _strategy,\\n       Types.Executable[] memory _actionExecs ) internal {\\n        uint256 actionLen = _actionExecs.length;\\n    if (actionLen == 0) {\\n           revert InvalidActions();\\n        } else {\\n      uint256 idx = 0;\\n    do {\\n    _executeOnSubAccount(_wallet, _subAccount, _strategy,\\n    _actionExecs[idx]);\\n    unchecked {\\n    ++idx;\\n            }\\n          } while (idx < actionLen);\\n        }\\n      }\\n```\\n

Impact:

Recommended mitigation:
Add a reentrancy guard for `executeAutomationViaBot()` and `executeTrustedAutomation()`.
Vulnerability: Anyone can make creating strategies extremely expensive for the user
Severity: medium

Description:
In Console architecture, users can deploy spare subaccounts (Gnosis Safes) so that when they will subscribe to a strategy most of the gas spending would have been spent at a low-gas phase.\\n```\\n    function deploySpareSubAccount(address _wallet) external { address subAccount =\\n        SafeDeployer(addressProvider.safeDeployer()).deploySubAccount(_wallet);\\n             subAccountToWalletMap[subAccount] = _wallet; walletToSubAccountMap[_wallet].push(subAccount);\\n           // No need to update subAccountStatus as it is already set to false\\n              emit SubAccountAllocated(_wallet, subAccount);\\n                }\\n```\\n\\nImpact The issue is that anyone can call the deploy function and specify another user's wallet. While on the surface that sounds like donating gas costs, in practice this functionality can make operating with strategies prohibitively expensive. When users will subscribe to strategies, the StrategyRegistry will request a subaccount using this function:\\n```\\n  function requestSubAccount(address _wallet) external returns (address) {\\n      if (msg.sender != subscriptionRegistry) \\n        revert OnlySubscriptionRegistryCallable();\\n          // Try to find a subAccount which already exists\\n           address[] memory subAccountList = walletToSubAccountMap[_wallet];\\n```\\n\\nAt this point, the entire subaccount array will be copied from storage to memory. Therefore, attackers can fill the array with hundreds of elements at a low-gas time and make creation of strategies very difficult.

Impact:

Recommended mitigation:
Limit the amount of spare subaccount to something reasonable, like 10 Team Response: Removing the spare subaccount deployment Mitigation review: Attack surface has been removed.
Vulnerability: DCA Strategies build orders that may not be executable, wasting fees
Severity: medium

Description:
In `_buildInitiateSwapExecutable()`, DCA strategies determine the swap parameters for the CoW Swap. The code has recently been refactored so that there may be more than one active order simultaneously. The issue is that the function assumes the user's entire ERC20 balance to be available for the order being built.\\n```\\n    // Check if enough balance present to swap, else swap entire balance\\n        uint256 amountIn = (inputTokenBalance < params.amountToSwap) ? \\n          inputTokenBalance : params.amountToSwap;\\n```\\n\\nImpact This is a problem because if the previous order will be executed before the current order, there may not be enough funds to pull from the user to execute the swap. As a result, transaction execution fees are wasted.

Impact:

Recommended mitigation:
Ensure only one swap can be in-flight at a time, or deduct the in-flight swap amounts from the current balance.
Vulnerability: User will lose all Console functionality when upgrading their wallet and an upgrade target has not been set up
Severity: medium

Description:
Console supports upgrading of the manager wallet using the `upgradeWalletType()` function.\\n```\\n  function upgradeWalletType() external {\\n    if (!isWallet(msg.sender)) \\n    revert WalletDoesntExist(msg.sender); uint8 fromWalletType = _walletDataMap[msg.sender].walletType;\\n      _setWalletType(msg.sender, _upgradablePaths[fromWalletType]);\\n      emit WalletUpgraded(msg.sender, fromWalletType,\\n  _upgradablePaths[fromWalletType]);\\n  }\\n```\\n\\nNote that upgradablePaths are set by governance. There is a lack of check that the upgradable path is defined before performing the upgrade.\\n```\\n   function _setWalletType(address _wallet, uint8 _walletType) private {\\n    _walletDataMap[_wallet].walletType = _walletType;\\n    }\\n```\\n\\nIf _upgradablePaths[fromWalletType] is zero (uninitialized), the user's wallet type shall become zero too. However, zero is an invalid value, as defined by the isWallet() view function:\\n```\\n  function isWallet(address _wallet) public view returns (bool) \\n    { WalletData memory walletData = _walletDataMap[_wallet];\\n  if (walletData.walletType == 0 || walletData.feeToken == address(0)){\\n          return false;\\n        }\\n    return true;\\n  }\\n```\\n\\nImpact As a result, most of the functionality of Console is permanently broken when users upgrade their wallet when an upgrade path isn't set. They can salvage their funds if it is a Safe account, as they can still execute on it directly.

Impact:

Recommended mitigation:
When settings a new wallet type, make sure the new type is not zero.
Vulnerability: Rounding error causes an additional iteration of DCA strategies
Severity: low

Description:
Both CoW strategies receive an interval and total amountIn of tokens to swap. They calculate the amount per iteration as below:\\n```\\n  Types.TokenRequest[] memory tokens = new Types.TokenRequest[](1); \\n      tokens[0] = Types.TokenRequest({token: inputToken, amount: amountIn});\\n       amountIn = amountIn / iterations;\\n  StrategyParams memory params = StrategyParams({ tokenIn: inputToken,\\n    tokenOut: outputToken, amountToSwap: amountIn, interval: interval, remitToOwner: remitToOwner\\n   });\\n```\\n

Impact:

Recommended mitigation:
Change the amount requested from the management wallet to amountIn / iterations * iterations.
Vulnerability: Fee mismatch between contracts can make strategies unusable
Severity: low

Description:
In CoW Swap strategies, fee is set in the strategy contracts and then passed to `initiateSwap()`. It is built in _buildInitiateSwapExecutable():\\n```\\n  // Generate executable to initiate swap on DCACoWAutomation return Types.Executable({\\n        callType: Types.CallType.DELEGATECALL, target: dcaCoWAutomation,\\n     value: 0,\\n  data: abi.encodeCall( DCACoWAutomation.initiateSwap,\\n        (params.tokenIn, params.tokenOut, swapRecipient, amountIn, minAmountOut, swapFee)\\n       )\\n    });\\n```\\n\\nThere is a mismatch between the constraints around fees between the strategy contracts and the `initiateSwap()` function:\\n```\\n    function setSwapFee(uint256 _swapFee) external {\\n        _onlyGov();\\n    if (_swapFee > 10_000) { revert InvalidSlippage();\\n       }\\n    swapFee = _swapFee;\\n      }\\n    if (feeBps > 0) {\\n    if (feeBps > 1_000) revert FeeTooHigh();\\n      amountIn = amountToSwap * (MAX_BPS - feeBps) / MAX_BPS;\\n```\\n

Impact:

Recommended mitigation:
Enforce the same constraints on the fee percentage in both contracts, or remove the check from one of them as part of a simplified security model.
Vulnerability: Reentrancy protection can likely be bypassed
Severity: high

Description:
The KeyManager offers reentrancy protection for interactions with the associated account. Through the LSP20 callbacks or through the `execute()` calls, it will call `_nonReentrantBefore()` before execution, and `_nonReentrantAfter()` post-execution. The latter will always reset the flag signaling entry.\\n```\\n    function _nonReentrantAfter() internal virtual {\\n    // By storing the original value once again, a refund is triggered \\n             (see // https://eips.ethereum.org/EIPS/eip-2200)\\n        _reentrancyStatus = false;\\n     }\\n```\\n\\nAn attacker can abuse it to reenter provided that there exists some third-party contract with REENTRANCY_PERMISSION that performs some interaction with the contract. The attacker would trigger the third-party code path, which will clear the reentrancy status, and enable attacker to reenter. This could potentially be chained several times. Breaking the reentrancy assumption would make code that assumes such flows to be impossible to now be vulnerable.

Impact:

Recommended mitigation:
In `_nonReentrantAfter()`, the flag should be returned to the original value before reentry, rather than always setting it to false.
Vulnerability: LSP20 verification library deviates from spec and will accept fail values
Severity: medium

Description:
The functions `lsp20VerifyCall()` and `lsp20VerifyCallResult()` are called to validate the owner accepts some account interaction. The specification states they must return a specific 4 byte magic value. However, the implementation will accept any byte array that starts with the required magic value.\\n```\\n    function _verifyCall(address logicVerifier) internal virtual returns (bool verifyAfter) {\\n        (bool success, bytes memory returnedData) = logicVerifier.call(\\n            abi.encodeWithSelector(ILSP20.lsp20VerifyCall.selector, msg.sender, msg.value, msg.data)\\n    );\\n    if (!success) _revert(false, returnedData);\\n    if (returnedData.length < 32) revert \\n      LSP20InvalidMagicValue(false, returnedData);\\n        bytes32 magicValue = abi.decode(returnedData, (bytes32));\\n    if (bytes3(magicValue) != \\n      bytes3(ILSP20.lsp20VerifyCall.selector))\\n        revert LSP20InvalidMagicValue(false, returnedData);\\n    return bytes1(magicValue[3]) == 0x01 ? true : false;\\n    }\\n```\\n\\nTherefore, implementations of the above functions which intend to signal failure status may be accepted by the verification wrapper above.

Impact:

Recommended mitigation:
Verify that the return data length is 32 bytes (the 4 bytes are extended by the compiler), and that all other bytes are zero.
Vulnerability: Deviation from spec will result in dislocation of receiver delegate
Severity: medium

Description:
The LSP0 `universalReceiver()` function looks up the receiver delegate by crafting a mapping key type.\\n```\\n  bytes32 lsp1typeIdDelegateKey = LSP2Utils.generateMappingKey(\\n  _LSP1_UNIVERSAL_RECEIVER_DELEGATE_PREFIX, bytes20(typeId));\\n```\\n\\nMapping keys are constructed of a 10-byte prefix, 2 zero bytes and a 20-byte suffix. However, followers of the specification will use an incorrect suffix. The docs do not discuss the trimming of bytes32 into a bytes20 type. The mismatch may cause various harmful scenarios when interacting with the delegate not using the reference implementation.

Impact:

Recommended mitigation:
Document the trimming action in the LSP0 specification.
Vulnerability: KeyManager ERC165 does not support LSP20
Severity: medium

Description:
LSP6KeyManager supports LSP20 call verification. However, in `supportInterface()` it does not return the LSP20 interfaceId.\\n```\\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\\n    return\\n    interfaceId == _INTERFACEID_LSP6 || interfaceId == _INTERFACEID_ERC1271 ||\\n        super.supportsInterface(interfaceId);\\n           }\\n```\\n\\nAs a result, clients which correctly check for support of LSP20 methods will not operate with the KeyManager implementation.

Impact:

Recommended mitigation:
Insert another supported interfaceId under `supportsInterface()`.
Vulnerability: LSP0 ownership functions deviate from specification and reject native tokens
Severity: low

Description:
The LSP specifications define the following functions for LSP0:\\n```\\n    function transferOwnership(address newPendingOwner) external payable;\\n    function renounceOwnership() external payable;\\n```\\n\\nHowever, their implementations are not payable.\\n```\\n    function transferOwnership(address newOwner) public virtual\\n        override(LSP14Ownable2Step, OwnableUnset)\\n    {\\n```\\n\\n```\\n    function renounceOwnership() public virtual override(LSP14Ownable2Step, OwnableUnset) {\\n         address _owner = owner();\\n```\\n\\nThis may break interoperation between conforming and non-confirming contracts.

Impact:

Recommended mitigation:
Remove the payable keyword in the specification for the above functions, or make the implementations payable
Vulnerability: Transfers of vaults from an invalid source are not treated correctly by receiver delegate
Severity: low

Description:
In the universalReceiver() function, if the notifying contract does not support LSP9, yet the typeID corresponds to an LSP9 transfer, the function will return instead of reverting.\\n```\\n    if (\\n      mapPrefix == _LSP10_VAULTS_MAP_KEY_PREFIX && notifier.code.length > 0 &&\\n         !notifier.supportsERC165InterfaceUnchecked(_INTERFACEID_LSP9)\\n    ) {\\n        return "LSP1: not an LSP9Vault ownership transfer";\\n     }\\n```\\n

Impact:

Recommended mitigation:
Revert when dealing with transfers that cannot be valid.
Vulnerability: Relayer can choose amount of gas for delivery of message
Severity: low

Description:
LSP6 supports relaying of calls using a supplied signature. The encoded message is defined as:\\n```\\n       bytes memory encodedMessage = abi.encodePacked( LSP6_VERSION,\\n          block.chainid,\\n             nonce,\\n                  msgValue,\\n        payload\\n    );\\n```\\n\\nThe message doesn't include a gas parameter, which means the relayer can specify any gas amount. If the provided gas is insufficient, the entire transaction will revert. However, if the called contract exhibits different behavior depending on the supplied gas, a relayer (attacker) has control over that behavior.

Impact:

Recommended mitigation:
Signed message should include the gas amount passed. Care should be taken to verify there is enough gas in the current state for the gas amount not to be truncated due to the 63/64 rule.