TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: high
Invalid

User who staked in `TempleGoldStaking` contract will not get voting points

Summary

Due to the default map values, users who stake into the TempleGoldStaking contract will not accumulate any voting points.

Vulnerability Details

In TempleGoldStaking contract, votes are stored in "checkpoints", when users stake in the contract, they have their staked amount and index stored in the numCheckpoints and _checkpoints variable via the _writeCheckpoint internal function, which is only called by _moveDelegates.

This _moveDelegates function is invoked whenever user stakes or withdraws from the protocol, as a reflection of user balance change. In stakeFor function, we see that, after _applyStake, _moveDelegates is called immediately:

function stakeFor(address _for, uint256 _amount) public whenNotPaused {
if (_amount == 0) revert CommonEventsAndErrors.ExpectedNonZero();
// pull tokens and apply stake
stakingToken.safeTransferFrom(msg.sender, address(this), _amount);
uint256 _lastIndex = _accountLastStakeIndex[_for];
_accountLastStakeIndex[_for] = ++_lastIndex;
_applyStake(_for, _amount, _lastIndex);
_moveDelegates(address(0), delegates[_for], _amount);
}

The parameters passed into this function corresponds to the source delegator, destination delegator, and the amount to delegate. The issue arises here, when an user stakes for the first time, he would have no delegator set previously, which makes delegates[_for] to return zero address as default value. This zero address is then passed into _moveDelegates, and nothing will happen:

function _moveDelegates(
address srcRep,
address dstRep,
uint256 amount
) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
uint256 srcRepNum = numCheckpoints[srcRep];
uint256 srcRepOld = srcRepNum > 0 ? _checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint256 srcRepNew = srcRepOld - amount;
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
uint256 dstRepNum = numCheckpoints[dstRep];
uint256 dstRepOld = dstRepNum > 0 ? _checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint256 dstRepNew = dstRepOld + amount;
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}

This is because the only way to execute codes in the if block is when the source and destination address are different, AND the delegate amount is greater than 0. Since with the default value in the map, both source and destination address will be zero, which makes the function do nothing. As a result, _writeCheckpoint will not be called, and as the only function to store voting information, this causes users to get no voting power regardless how much they have staked.

Impact

Users will not get voting powers despite staking in the contract.

Tools Used

Manual review

Recommendations

Check for zero address when users are staking, and use staker's address if the delegating address is zero address.

Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

y4y Submitter
11 months ago
inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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