Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Valid

Users don't have to stake inside the gauges to earn rewards

Summary

Deposit and withdraw are useless

Vulnerability Details

Users don't have to stake inside the gauges to earn rewards. That is because both deposit and withdraw don't do anything but take tokens and record those balances inside 2 variables _totalSupply and _balances[user], which are never used

//@finding H users don't even have to stake to earn rewards, they earn them by just having bias
function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {
if (amount == 0) revert InvalidAmount();
_totalSupply += amount;
_balances[msg.sender] += amount;
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}
function withdraw(uint256 amount) external nonReentrant updateReward(msg.sender) {
if (amount == 0) revert InvalidAmount();
if (_balances[msg.sender] < amount) revert InsufficientBalance();
_totalSupply -= amount;
_balances[msg.sender] -= amount;
stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}

Instead users earn rewards by just having veTokens:

function earned(address account) public view returns (uint256) {
// 10k * (rewardPerTokenGlobal - rewardPerTokenUser) / 1e18
return (getUserWeight(account) *
(getRewardPerToken() - userStates[account].rewardPerTokenPaid) / 1e18
) + userStates[account].rewards;
}

The even worse thing is that reward math takes into account _totalSupply to divide the rewards, but doesn't use _balances[user] to allocate each user rewards.

function getRewardPerToken() public view returns (uint256) {
//@almost what if rewards start before there are any deposits, will the first depositor claim them all ?
// as on deposit the time will not be updated
if (totalSupply() == 0) {
return rewardPerTokenStored;
}
// rewardPerTokenStored + (timeDiff * rewardRate * 1e18 / _totalSupply)
return rewardPerTokenStored + (
(lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18 / totalSupply()
);
}

Impact

Contract does not work.
Reward math is broken
There are no incentives to deposit.

Tools Used

Manual review

Recommendations

Base rewards off user balances.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge::earned calculates rewards using getUserWeight instead of staked balances, potentially allowing users to claim rewards by gaining weight without proper reward checkpoint updates

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge::earned calculates rewards using getUserWeight instead of staked balances, potentially allowing users to claim rewards by gaining weight without proper reward checkpoint updates

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!