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

Reentrancy Risk in _withdraw

Summary

A comprehensive security audit of PerpetualVault.sol has identified several critical vulnerabilities and design concerns, with the most severe being potential reentrancy attacks in withdrawal functions.

Vulnerability Details

  1. Reentrancy Vulnerability in Withdrawal Flow

function _withdraw(uint256 depositId, bytes memory metadata, MarketPrices memory prices) internal {
uint256 shares = depositInfo[depositId].shares;
// ... state checks ...
if (_isLongOneLeverage(beenLong)) {
// External calls before state updates
uint256 swapAmount = IERC20(indexToken).balanceOf(address(this)) * shares / totalShares;
nextAction.selector = NextActionSelector.SWAP_ACTION;
nextAction.data = abi.encode(swapAmount, false);
}
// ... more external interactions ...
}

The withdrawal flow makes external calls through token transfers and interactions with external protocols before updating critical state variables, creating potential reentrancy vectors.

  1. Missing State Validation

function _handleReturn(uint256 withdrawn, bool positionClosed, bool refundFee) internal {
(uint256 depositId) = flowData;
uint256 shares = depositInfo[depositId].shares;
// ... state changes without validation ...
}

Impact

  1. Critical: Potential reentrancy attacks could lead to:

    • Double withdrawals

    • Manipulation of share calculations

    • Theft of user funds

    • Protocol insolvency

  2. High: Missing state validations could result in:

    • Incorrect share calculations

    • Unauthorized withdrawals

    • Broken accounting

Tools Used

  • Manual code review

  • Echidna for property-based testing

  • Foundry for unit testing

Recommendations

  1. Implement Reentrancy Guards

modifier nonReentrantWithdraw() {
require(_withdrawLock == 1, "Reentrant call");
_withdrawLock = 2;
_;
_withdrawLock = 1;
}
  1. Follow CEI Pattern

function _withdraw(uint256 depositId, bytes memory metadata, MarketPrices memory prices) internal {
// 1. Checks
uint256 shares = depositInfo[depositId].shares;
require(shares > 0, "Invalid shares");
// 2. Effects
totalShares -= shares;
depositInfo[depositId].shares = 0;
// 3. Interactions
if (_isLongOneLeverage(beenLong)) {
_performSwap(shares);
}
}
  1. Add State Validations

function _handleReturn(uint256 withdrawn, bool positionClosed, bool refundFee) internal {
require(flow != FLOW.NONE, "Invalid flow");
require(flowData != 0, "Invalid deposit ID");
// ... existing code ...
}
  1. Implement Circuit Breakers

uint256 public constant MAX_WITHDRAWAL_AMOUNT = 1000000e18;
function _withdraw(uint256 depositId, bytes memory metadata, MarketPrices memory prices) internal {
require(withdrawn <= MAX_WITHDRAWAL_AMOUNT, "Withdrawal too large");
// ... rest of the function
}
  1. Add Event Emissions for Critical State Changes

event WithdrawalProcessed(
uint256 indexed depositId,
uint256 shares,
uint256 amount,
address recipient
);
Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

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

Give us feedback!