Core Contracts

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

Missing WhenNotPaused Modifier in BaseGauge Stake and Withdraw Functions: Unrestricted Operations During Emergency

Summary

In the BaseGauge contract, the functions stake and withdraw allow users to deposit and remove tokens from the gauge, respectively. These functions play a crucial role in updating user balances, total staked amounts, and reward calculations. However, both functions are missing the whenNotPaused modifier. The absence of this modifier means that even if the contract is paused—a state used to halt operations during emergencies—users can continue to stake and withdraw tokens. This oversight bypasses an important security control, potentially enabling uncontrolled token movements during critical conditions and undermining the protocol’s emergency safeguards.

Vulnerability Details

How It Begins

  • Intended Behavior:
    In a pausable contract, operations that modify critical state (e.g., staking and withdrawal) must be disabled when the system is paused to prevent undesired interactions during emergencies. The whenNotPaused modifier is used to enforce this behavior.

  • Current Implementation:
    The stake and withdraw functions in the BaseGauge contract use the nonReentrant and updateReward modifiers but omit the whenNotPaused modifier. As a result, these functions remain callable even when the contract is paused.

Code Analysis

Stake Function:

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);
}
  • Issue:
    The function does not include whenNotPaused. Therefore, staking can occur regardless of the contract’s paused state.

Withdraw Function:

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);
}
  • Issue:
    Similarly, the withdrawal function does not include whenNotPaused, enabling token withdrawals even when operations should be halted.

Consequences

  • Bypassing Emergency Controls:
    Without the whenNotPaused modifier, the contract’s pause mechanism is rendered ineffective for these functions. This permits users to move tokens even in emergency situations when the protocol should be in a secure, halted state.

  • Risk of Exploitation:
    Attackers might exploit this flaw during periods of instability or emergency to manipulate token balances, potentially draining the system or interfering with reward and governance calculations.

  • Protocol Integrity:
    Uncontrolled staking and withdrawal operations during a pause can lead to unexpected changes in user balances and the overall state of the gauge, potentially destabilizing reward distribution and governance mechanisms.

Impact

  • Uncontrolled Token Movements:
    Staking and withdrawal operations can occur during emergency conditions, leading to unanticipated asset flows.

  • Security Risk:
    Attackers might exploit this oversight to manipulate token balances, bypassing emergency protocols intended to protect the system.

  • Governance and Economic Distortion:
    Unrestricted token movements during a pause could affect reward calculations and governance weight, destabilizing the protocol's economic incentives.

  • Loss of Trust:
    The inability to enforce pause conditions on critical functions can undermine confidence in the protocol’s emergency mechanisms and overall security.

Tools Used

  • Manual Review

Recommendations

To secure the BaseGauge contract during emergency pauses, add the whenNotPaused modifier to both the stake and withdraw functions.

Proposed Diff for stake Function

- function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {
+ function stake(uint256 amount) external nonReentrant whenNotPaused 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);
}

Proposed Diff for withdraw Function

- function withdraw(uint256 amount) external nonReentrant updateReward(msg.sender) {
+ function withdraw(uint256 amount) external nonReentrant whenNotPaused 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);
}
Updates

Lead Judging Commences

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

BaseGauge::withdraw, stake, and checkpoint functions lack whenNotPaused modifier, allowing critical state changes even during emergency pause

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

BaseGauge::withdraw, stake, and checkpoint functions lack whenNotPaused modifier, allowing critical state changes even during emergency pause

Support

FAQs

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

Give us feedback!