DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: high
Invalid

Critical Risk Due to Manual Deposit Pause Management

Summary

The PerpetualVault contract pauses all deposits upon liquidation by setting depositPaused = true. However, this state is only reverted manually by the admin through the setDepositPaused function. If the admin is unavailable, delayed, or unable to access the contract, the vault could remain in a paused state indefinitely, causing an extended deposit denial for all users.

Vulnerability Details

  • The afterLiquidationExecution function sets depositPaused to true for safety reasons when a liquidation event occurs.

  • The contract lacks an automated mechanism to revert depositPaused to false after conditions normalize.

  • Only the admin can manually reset depositPaused to false via the setDepositPaused function.

  • If the admin is unavailable, forgets to unpause, or loses access, the deposit functionality remains blocked, resulting in a protocol-level denial-of-service (DoS).

Potential Real-World Scenarios:

  • Admin Inaccessibility: Admin loses access to their wallet or is unavailable due to unforeseen circumstances.

  • Operational Oversight: Admin overlooks the need to call setDepositPaused(false) after liquidation resolution.

  • Delayed Response: Admin faces delays due to technical, logistical, or organizational reasons.

Code Snippet (Vulnerable Code)-

function afterLiquidationExecution() external {
if (msg.sender != address(gmxProxy)) {
revert Error.InvalidCall();
}
depositPaused = true; // All deposits paused globally
uint256 sizeInTokens = vaultReader.getPositionSizeInTokens(curPositionKey);
if (sizeInTokens == 0) {
delete curPositionKey;
}
}

Impact

  1. Vault-Wide Deposit Denial:
    When liquidation occurs, depositPaused is set to true, blocking deposits from all users — not just the affected position. This disrupts normal operations for unaffected users.

  2. Operational Dependency on Admin:
    Since only the admin can revert depositPaused to false, the contract's functionality becomes dependent on the admin's availability.

  3. Potential Loss of Revenue and User Trust:
    If deposits remain paused for extended periods, potential new users and capital inflows are blocked, leading to revenue loss and potential user dissatisfaction.

Tools Used

Manual Review

Recommendations

Implement Auto-Unpause Mechanism

Automatically revert depositPaused after a cooldown period:

uint256 public lastPausedTimestamp;
function afterLiquidationExecution() external {
if (msg.sender != address(gmxProxy)) {
revert Error.InvalidCall();
}
depositPaused = true;
lastPausedTimestamp = block.timestamp;
// Remaining logic
}
function autoUnpause() external {
require(block.timestamp >= lastPausedTimestamp + 1 days, "Cooldown not reached");
depositPaused = false;
}

Introduce Multiple Admins for Redundancy:

Utilize OpenZeppelin's AccessControl to assign backup roles:

bytes32 public constant BACKUP_ADMIN_ROLE = keccak256("BACKUP_ADMIN_ROLE");
function setDepositPaused(bool _paused) external {
require(
hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(BACKUP_ADMIN_ROLE, msg.sender),
"Caller is not authorized"
);
depositPaused = _paused;
}

Benefit of Mitigation:
The vault's deposit functionality can experience prolonged downtime due to the manual nature of unpausing after liquidation events. While the protocol acknowledges certain DoS risks, this issue stems from the internal logic of the afterLiquidationExecution function rather than malicious actor behavior. Implementing automatic unpause mechanisms and redundant admin roles can significantly mitigate the risk.

Updates

Lead Judging Commences

n0kto Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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