Immediately start getting rewards belonging to others after staking
Note This issue affects both LMPVault and DV since they use the same underlying reward contract.\\nAssume a new user called Bob mints 100 LMPVault or DV shares. The ERC20's `_mint` function will be called, which will first increase Bob's balance at Line 267 and then trigger the `_afterTokenTransfer` hook at Line 271.\\n```\\nFile: ERC20.sol\\n    function _mint(address account, uint256 amount) internal virtual {\\n..SNIP..\\n        _beforeTokenTransfer(address(0), account, amount);\\n\\n        _totalSupply += amount;\\n        unchecked {\\n            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\\n            _balances[account] += amount;\\n        }\\n..SNIP..\\n        _afterTokenTransfer(address(0), account, amount);\\n    }\\n```\\n\\nThe `_afterTokenTransfer` hook will automatically stake the newly minted shares to the rewarder contracts on behalf of Bob.\\n```\\nFile: LMPVault.sol\\n    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override {\\n..SNIP..\\n        if (to != address(0)) {\\n            rewarder.stake(to, amount);\\n        }\\n    }\\n```\\n\\nWithin the `MainRewarder.stake` function, it will first call the `_updateReward` function at Line 87 to take a snapshot of accumulated rewards. Since Bob is a new user, his accumulated rewards should be zero. However, this turned out to be false due to the bug described in this report.\\n```\\nFile: MainRewarder.sol\\n    function stake(address account, uint256 amount) public onlyStakeTracker {\\n        _updateReward(account);\\n        _stake(account, amount);\\n\\n        for (uint256 i = 0; i < extraRewards.length; ++i) {\\n            IExtraRewarder(extraRewards[i]).stake(account, amount);\\n        }\\n    }\\n```\\n\\nWhen the `_updateReward` function is executed, it will compute Bob's earned rewards. It is important to note that at this point, Bob's balance has already been updated to 100 shares in the `stakeTracker` contract, and `userRewardPerTokenPaid[Bob]` is zero.\\nBob's earned reward will be as follows, where $r$ is the rewardPerToken():\\n$$ earned(Bob) = 100\\ {shares \\times (r - 0)} = 100r $$\\nBob immediately accumulated a reward of $100r$ upon staking into the rewarder contract, which is incorrect. Bob could withdraw $100r$ reward tokens that do not belong to him.\\n```\\nFile: AbstractRewarder.sol\\n    function _updateReward(address account) internal {\\n        uint256 earnedRewards = 0;\\n        rewardPerTokenStored = rewardPerToken();\\n        lastUpdateBlock = lastBlockRewardApplicable();\\n\\n        if (account != address(0)) {\\n            earnedRewards = earned(account);\\n            rewards[account] = earnedRewards;\\n            userRewardPerTokenPaid[account] = rewardPerTokenStored;\\n        }\\n\\n        emit UserRewardUpdated(account, earnedRewards, rewardPerTokenStored, lastUpdateBlock);\\n    }\\n..SNIP..\\n    function balanceOf(address account) public view returns (uint256) {\\n        return stakeTracker.balanceOf(account);\\n    }\\n..SNIP..\\n    function earned(address account) public view returns (uint256) {\\n        return (balanceOf(account) * (rewardPerToken() - userRewardPerTokenPaid[account]) / 1e18) + rewards[account];\\n    }\\n```\\n
Ensure that the balance of the users in the rewarder contract is only incremented after the `_updateReward` function is executed.\\nOne option is to track the balance of the staker and total supply internally within the rewarder contract and avoid reading the states in the `stakeTracker` contract, commonly seen in many reward contracts.\\n```\\nFile: AbstractRewarder.sol\\nfunction balanceOf(address account) public view returns (uint256) {\\n// Remove the line below\\n   return stakeTracker.balanceOf(account);\\n// Add the line below\\n return _balances[account];\\n}\\n```\\n\\n```\\nFile: AbstractRewarder.sol\\nfunction _stake(address account, uint256 amount) internal {\\n    Errors.verifyNotZero(account, "account");\\n    Errors.verifyNotZero(amount, "amount");\\n    \\n// Add the line below\\n    _totalSupply // Add the line below\\n= amount\\n// Add the line below\\n    _balances[account] // Add the line below\\n= amount\\n\\n    emit Staked(account, amount);\\n}\\n```\\n
Loss of reward tokens for the vault shareholders.
```\\nFile: ERC20.sol\\n    function _mint(address account, uint256 amount) internal virtual {\\n..SNIP..\\n        _beforeTokenTransfer(address(0), account, amount);\\n\\n        _totalSupply += amount;\\n        unchecked {\\n            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\\n            _balances[account] += amount;\\n        }\\n..SNIP..\\n        _afterTokenTransfer(address(0), account, amount);\\n    }\\n```\\n