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

Deposit Pause After Liquidation

Summary

When a position is liquidated, the PerpetualVault contract permanently sets depositPaused to true, requiring manual intervention to resume deposits. This could lead to extended periods where the vault remains inaccessible to new deposits, impacting system availability.

Vulnerability Details

The issue is in the afterLiquidationExecution function in PerpetualVault.sol (lines 665-683):

function afterLiquidationExecution() external {
if (msg.sender != address(gmxProxy)) {
revert Error.InvalidCall();
}
depositPaused = true;
uint256 sizeInTokens = vaultReader.getPositionSizeInTokens(curPositionKey);
if (sizeInTokens == 0) {
delete curPositionKey;
}
if (flow == FLOW.NONE) {
flow = FLOW.LIQUIDATION;
nextAction.selector = NextActionSelector.FINALIZE;
} else if (flow == FLOW.DEPOSIT) {
flowData = sizeInTokens;
} else if (flow == FLOW.WITHDRAW) {
// restart the withdraw flow even though current step is FINALIZE.
nextAction.selector = NextActionSelector.WITHDRAW_ACTION;
}
}

When a position is liquidated, the contract sets depositPaused to true but provides no automatic mechanism to reset this state. The only way to resume deposits is for the contract owner to manually call setDepositPaused(false).

Impact

If a liquidation occurs and the contract owner does not promptly respond to unpause deposits, users will be unable to deposit into the vault for an extended period. This creates both:

  1. A potential denial of service for new deposits

  2. A dependency on active monitoring and quick response from contract administrators

  3. Potential lost opportunity costs for users who cannot deposit during favorable market conditions

While this doesn't directly risk existing user funds, it could significantly impact the protocol's usability and reliability, especially if liquidations happen during off-hours or periods of high market volatility when administrator response might be delayed.

Tools Used

Manual code review

Recommendations

Implement one of the following options:

  • Automatic reset of deposit pause after liquidation recovery:

// Add a function to finalize liquidation recovery and reset deposit pause
function finalizeLiquidationRecovery() external onlyKeeper {
require(flow == FLOW.LIQUIDATION, "Not in liquidation flow");
require(nextAction.selector == NextActionSelector.NO_ACTION, "Liquidation recovery not complete");
// Reset liquidation state
flow = FLOW.NONE;
depositPaused = false;
emit LiquidationRecoveryComplete();
}
  • Set a time-based automatic reset:

uint256 public depositPauseExpiry;
uint256 public constant DEPOSIT_PAUSE_DURATION = 24 hours;
function afterLiquidationExecution() external {
if (msg.sender != address(gmxProxy)) {
revert Error.InvalidCall();
}
depositPaused = true;
depositPauseExpiry = block.timestamp + DEPOSIT_PAUSE_DURATION;
// Rest of function remains the same
}
// Modify deposit function to check expiry
function deposit(uint256 amount) external nonReentrant payable {
_noneFlow();
if (depositPaused == true && block.timestamp < depositPauseExpiry) {
revert Error.Paused();
}
// Rest of deposit function remains the same
}
  • At minimum, add an event when deposits are paused to improve monitoring:

event DepositsPaused(bool paused);
function afterLiquidationExecution() external {
// Existing code
depositPaused = true;
emit DepositsPaused(true);
// Rest of function
}
Updates

Lead Judging Commences

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

Support

FAQs

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

Give us feedback!