TempleGold

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

Potential DoS via manipulation of checkpoints in `TempleGoldStaking` contract

Summary

A user can stake a very small amount (dust amount) and manipulate the checkpoints, potentially causing a Denial of Service (DoS) attack on the TempleGoldStaking.getPriorVotes() .

Vulnerability Details

The TempleGoldStaking contract allows users to stake dust amounts of tokens.

An attacker can exploit this by staking dust amounts and repeatedly changing their delegatee to any victim address to create a large number of checkpoints.

This increases the computational load on the TempleGoldStaking.getPriorVotes() , potentially leading to performance issues and a Denial of Service (DoS).

// Location: TempleGoldStaking.sol
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);
}
// Location: TempleGoldStaking.sol
function delegate(address delegatee) external override { //@follow-up
return _delegate(msg.sender, delegatee);
}
// snipped
function _delegate(address delegator, address delegatee) internal {
address currentDelegate = delegates[delegator];
uint256 delegatorBalance = _balances[delegator];
delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
@> _moveDelegates(currentDelegate, delegatee, delegatorBalance);
}
// Location: TempleGoldStaking.sol
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);
}
}
}
function _writeCheckpoint(
address delegatee,
uint256 nCheckpoints,
uint256 oldVotes,
uint256 newVotes
) internal {
if (nCheckpoints > 0 && _checkpoints[delegatee][nCheckpoints - 1].fromBlock == block.number) {
_checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
} else {
@> _checkpoints[delegatee][nCheckpoints] = Checkpoint(block.number, newVotes);
numCheckpoints[delegatee] = nCheckpoints + 1;
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}

Impact

An attacker can cause TempleGoldStaking.getPriorVotes() to incur high gas costs, potentially leading to a Denial of Service (DoS) attack.

Proof of Concept

  • Initial State: Attacker has not set their delegatee

    • delegates[attacker] = address(0)

  • Step 1: Attacker stake 1 wei TGLD via the TempleGoldStaking.stake()

    • _balances[attacker] += 1 wei = 1

    • No delegates move as src: address(0) == dest: address(0)

  • Step 2: Attacker delegate their vote to Alice => TempleGoldStaking.delegate(address(Alice))

    • Previous delegatee = address(0)

    • Introduce the new Alice Checkpoint struct

    • numCheckpoints[Alice] is increased

  • Step 3: Attacker delegate their vote to Bob => TempleGoldStaking.delegate(address(Bob))

    • Previous delegatee = address(Alice)

    • Introduce the new Alice Checkpoint struct

    • Introduce the new Bob Checkpoint struct

    • numCheckpoints[Alice] is increased

    • numCheckpoints[Bob] is increased

  • Step 4: Repeat Step 2 and 3 to repeatedly change the delegatee between Alice and Bob, increasing numCheckpoints and adding Checkpoints for both.

Tools Used

  • Manual Review

Recommendations

Introduce a minimum staking amount requirement in the TempleGoldStaking,stakeFor() to prevent users from staking dust amounts.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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