TempleGold

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

Potential Reentrancy Attack in `stakeFor` Function in `TempleGoldStaking`contract

Summary:

The stakeFor function involves transferring tokens and updating state variables. The current implementation of the function allows for a reentrancy attack because it does not follow the Check-Effects-Interactions (CEI) pattern. Ensuring that all state updates are completed before any external calls are made is crucial to prevent such attacks.

Vulnerability Details:

found in stakeFor function in the TempleGoldStaking.sol smart contract.

Impact:

If a reentrancy attack is possible, an attacker could exploit the contract to call the function repeatedly before the initial execution completes, potentially leading to inconsistencies and unauthorized operations, resulting in loss of funds or other unexpected behaviors.

Code Analysis

The function stakeFor currently looks like this:

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);
}

In this code, the safeTransferFrom call is an external interaction that occurs before state updates are fully completed, which violates the CEI pattern.

Proof of Concept (PoC) / Scenario

  • An attacker could craft a malicious stakingToken contract to re-enter the stakeFor function during the safeTransferFrom call.

Tools Used:

Manual audit, Foundry

Recommendations:

To prevent reentrancy attacks, follow the CEI pattern: perform all state changes before making external calls. Reorder the function to update the state variables before the external call.

Example Mitigation:

function stakeFor(address _for, uint256 _amount) public whenNotPaused {
if (_amount == 0) revert CommonEventsAndErrors.ExpectedNonZero();
// Update state before external call
uint256 _lastIndex = _accountLastStakeIndex[_for];
_accountLastStakeIndex[_for] = ++_lastIndex;
// External call after state update
stakingToken.safeTransferFrom(msg.sender, address(this), _amount);
_applyStake(_for, _amount, _lastIndex);
_moveDelegates(address(0), delegates[_for], _amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Appeal created

0xdhanraj30 Submitter
11 months ago
inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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