Skip to content
Request an audit

‹ All findings

VaultPoolLib::reserve() will store the Pa not attributed to user withdrawals incorrectly and leave in untracked once it expires again

Crit/HighCork Protocol·Sherlock · Derivatives · 29th August 2024CodebaseH-11

Summary

VaultPoolLib::reserve() stores the Pa attributed to withdrawals in self.withdrawalPool.stagnatedPaBalance instead of storing the amount attributedToAmm. Additionally, this amount of Pa, the one attributed to the Amm is never dealt with and leads to stuck PA.

The comment in the code mentions

solidity
    // FIXME : this is only temporary, for now
    // we trate PA the same as RA, thus we also separate PA
    // the difference is the PA here isn't being used as anything
    // and for now will just sit there until rationed again at next expiry.

But it is incorrect as it is never rationed again, just forgotten. The VaultPoolLib::rationedToAmm() function only uses the Ra balance, not the Pa, which is effectively left untracked.

Root Cause

In VaultPoolLib:170, the leftover non attributed Pa is not dealt with.

Internal pre-conditions

None.

External pre-conditions

None.

Attack Path

  1. VaultPoolLib::reserve() is called when liquidating the lp position of the Vault via VaultLib::_liquidatedLP(), triggered by users when redeeming expired liquidity vault shares or on the admin trigerring a new issuance.

Impact

The Pa in the Vault is stuck.

PoC

VaultPoolLib::rationedToAmm() does not deal with the Pa.

solidity
function rationedToAmm(VaultPool storage self, uint256 ratio) internal view returns (uint256 ra, uint256 ct) {
    uint256 amount = self.ammLiquidityPool.balance;

    (ra, ct) = MathHelper.calculateProvideLiquidityAmountBasedOnCtPrice(amount, ratio);
}

Mitigation

Distributed the Pa to users based on their LV shares or redeem the Pa for Ra and add liquidity to the new issued Ds or similar.