Lines of code
<https://github.com/code-423n4/2023-03-wenwin/blob/main/src/staking/Staking.sol#L122>
The return amount of the function rewardPerToken() may be inflated for the first in the Staking.sol contract.
The Staking.sol contract is designed for the LOT token holders to be able to stake their (native) tokens. Thus, the token holders would be able to receive corresponding rewards from ticket sales. The Staking.sol contract which is a vault-like ERC20 contract, empowers users via staking their LOT tokens and receiving their rewards by the means of rewardsToken (DAI stablecoin).
The rewardPerToken() function is where the reward calculations happen. The return amount of this function is used in _updateReward(), and earned() functions. So it’ll better to look at what is inside the function rewardPerToken():
function rewardPerToken() public view override returns (uint256 _rewardPerToken) {
uint256 _totalSupply = totalSupply();
if (_totalSupply == 0) {
return rewardPerTokenStored;
}
uint256 ticketsSoldSinceUpdate = lottery.nextTicketId() - lastUpdateTicketId;
uint256 unclaimedRewards =
LotteryMath.calculateRewards(lottery.ticketPrice(), ticketsSoldSinceUpdate, LotteryRewardType.STAKING);
return rewardPerTokenStored + (unclaimedRewards * 1e18 / _totalSupply);
}
As one can see, the return part of the function, for the case in which totalSupply() is not zero, contains a multiplication for 1e18. Therefore, it may enhance the return amount drastically for the cases in which totalSupply() is not in the orders of 1e18.
Consider this situation:
1 - Suppose that the contract is deployed
2 - Bob who listens to the contract bytecode, calls the stake() function with the amount 1 Wei.
3 - The _mint() function is triggered, and thus calls the _beforeTokenTransfer() hook.
4 - The function _updateReward() for Bob’s address is called.
5 - As the initial totalSupply() for the Staking contract is 0, the rewards and userRewardPerTokenPaid mappings for the key of Bob’s address become 0 respectively.
6 - Bob calls the getReward() function then.
7 - This time, _updateReward() for Bob’s address give large number, as the earned() calls the rewardPerToken()for totalSupply() of 1.
8 - the rewards[Bob’s address] would be changed to a large number, and thus Bob will receive rewards.
For detailed explanation, as the earned() function is called before the userRewardPerTokenPaid mapping for Bob’s address, the rewards amount is calculated via the previous data:
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = currentRewardPerToken;
Pen & Paper
It might be considered to initialize the contract with some token amounts or put some boundaries for stakers rewards.
The text was updated successfully, but these errors were encountered:
All reactions