This vulnerability within PerpetualVault contract's withdrawal mechanism exposes user funds to permanent loss due to improper ERC20 transfer error handling. The PerpetualVault._transferToken() function fails to properly detect and handle USDC transfer failures, allowing funds to become irreversibly locked in the contract. The root cause stems from using try/catch blocks that only capture reverting transactions, while USDC (the designated collateral token) implements ERC20's boolean return pattern for error reporting. This critical mismatch between error handling implementation and token behavior violates core protocol functionality guarantees.
The vulnerability exists in the token transfer error handling within the PerpetualVault._transferToken function (PerpetualVault.sol#L1171-L1175). The implementation incorrectly uses a try/catch block to handle ERC20 transfer failures, which is ineffective for tokens like USDC that return boolean status codes rather than reverting on failed transfers.
This approach creates a silent failure scenario where:
Failed transfers to recipients don't trigger the fallback to treasury
Withdrawal transactions complete without properly moving funds
User balances get stuck in the contract indefinitely
The protocol's documentation explicitly states USDC is used as collateralToken, making this a protocol-specific vulnerability rather than a general ERC20 handling issue. The current implementation directly contradicts the stated design intent to redirect failed transfers to the treasury.
This vulnerability directly breaks core protocol functionality in a way that leads to irreversible financial losses for users:
Exploit Scenario:
User deposits 10,000 USDC
Later initiates withdrawal for 10,000 USDC
Recipient address is USDC-blacklisted (common regulatory compliance feature) or USDC contract paused
Transfer returns false but doesn't revert
Contract state updates complete (totalDepositAmount reduced)
10,000 USDC remains locked in contract permanently
User cannot retry withdrawal as deposit record is deleted in _handleReturn() that calls _transferToken()
Direct Impacts:
Permanent locking of user funds in contract when transfers fail
Inability to recover stuck funds due to state cleanup after failed transfers
Protocol accounting mismatch between internal records and actual token balances
Systemic Risks:
Complete loss of withdraw functionality for affected users
Protocol insolvency risk as locked funds accumulate
Erosion of user trust in withdrawal guarantees
Manual Review
Replace try/catch with return value check and use safeTransfer for treasury transfer:
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.
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.