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

Vault with 1X leverage does not delete `swapProgressData` after minting shares breaking the key invariant of the protocol

Summary

When a user calls deposit, execution fee is paid followed by INCREASE_ACTION if a position is already opened.
If runNextAction() is called by the keeper after that and vault is 1X leverage with only dexswap , it triggers _runSwap() followed by _mint() action. The call is completed but swapProgressData is not deleted violating the following invariant :

> After all actions completed, nextAction, swapProgressData should be empty. PositionKey is 0 when no positio

Vulnerability Details

This is our inital state :

  1. Vault is of 1X leverage

  2. Swap only happens with dex ( paraswap )

  3. There is an open position in the vault

Suppose Alice calls deposit() with 100 collateral tokens.

https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/PerpetualVault.sol#L215-L243

Since there is a position opened already, deposit will initiate INCREASE_ACTION.

```Solidity
if (positionIsClosed) {

MarketPrices memory prices;

_mint(counter, amount, false, prices);

_finalize(hex'');

} else {

_payExecutionFee(counter, true);

// mint share token in the NextAction to involve off-chain price data and improve security

nextAction.selector = NextActionSelector.INCREASE_ACTION;

nextAction.data = abi.encode(beenLong);

}
```

Keeper will call runNextAction() to increase position size. Keep in mind that flow is still DEPOSIT.

https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/PerpetualVault.sol#L350-L409

Since vault is of 1X leverage , _isLongOneLeverage(_isLong) will be true and _runSwap() will be initiate

https://github.com/CodeHawks-Contests/2025-02-gamma/blob/84b9da452fc84762378481fa39b4087b10bab5e0/contracts/PerpetualVault.sol#L976-L1019

Assume that we are only swapping for dexSwap i.e. metadata = 1 and PROTOCOL = PROTOCOL.DEX
So, the below condition will be executed


```Solidity
else {

// some code....

if (flow == FLOW.DEPOSIT) {

// last `depositId` equals with `counter` because another deposit is not allowed before previous deposit is completely processed

_mint(counter, outputAmount + swapProgressData.swapped, true, prices);

}
// more code
```
Now, when _mint is executed , the function proceed to calculate the amount of shares that should be given to Alice based on the output amount of paraswap. The execution completed but at no point , flow, flowdata and swapProgressdata was deleted.

This breaks the core invariant mentioned in README.

Impact

Invariant Voilation. subsequent deposits and withdraw will not happen since flow is not deleted.
If swapProgress is not deleted and minting of shares can be altered.

Tools Used

Manual Review

Recommendations

Make sure to delete these values after the action is completed.

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

phoenix Submitter
9 months ago
0xl33 Auditor
9 months ago
phoenix Submitter
9 months ago
n0kto Lead Judge
8 months ago
n0kto Lead Judge 8 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_deposit_1x_long_dex_positionIsOpened_DoS_Flow

Likelihood: Medium/High, - Leverage = 1x - beenLong = True - positionIsClosed = False - Metadata → 1 length and Dex Swap Impact: Medium/High, DoS on any new action before the admin uses setVaultState Since this seems to be the most probable path for a 1x PerpVault, this one deserves a High.

Support

FAQs

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

Give us feedback!