A vulnerability exists in the redeem
function where the locked credit capacity is not validated before processing withdrawals. This allows users to withdraw funds even when the vault is insolvent (i.e., when the locked credit capacity is zero), violating the protocol's intended behavior and putting the system at risk.
The redeem
function does not check if the locked credit capacity (ctx.lockedCreditCapacityBeforeRedeemUsdX18
) is non-zero before allowing withdrawals.
If the locked credit capacity is zero (indicating insolvency), the function proceeds with the withdrawal, even though the protocol's documentation explicitly states that funds should not be withdrawn when the vault is insolvent.
Although the system to check if the fund the user withdraw is more than the lockedCreditCapacity abd revert it's check if it's less
The issue is present in the redeem
function:
https://github.com/Cyfrin/2025-01-zaros-part-2/blob/35deb3e92b2a32cd304bf61d27e6071ef36e446d/src/market-making/branches/VaultRouterBranch.sol#L486
The protocol should prevent withdrawals when the vault is insolvent (i.e., when the locked credit capacity is zero).
https://github.com/Cyfrin/2025-01-zaros-part-2/blob/35deb3e92b2a32cd304bf61d27e6071ef36e446d/src/market-making/leaves/Vault.sol#L188
The protocol allows withdrawals even when the locked credit capacity is zero, violating the intended behavior and putting the system at risk.
Total Credit Capacity:
If totalDebt
exceeds (totalAssetInVault * adjustedPriceOfAsset)
, the result is negative, indicating the vault is insolvent.
Locked Credit Capacity:
If TotalCreditCapacityUsd
is negative, the locked credit capacity will also be negative (since multiplying a negative value by a positive ratio results in a negative value).
However, the getLockedCreditCapacityUsd
function explicitly sets the locked credit capacity to zero if TotalCreditCapacityUsd
is negative:
Let’s analyze the behavior when the vault is insolvent (TotalCreditCapacityUsd
is negative):
Initial State:
TotalCreditCapacityUsd = -1000
(vault is insolvent).
TotalCreditCapacityLocked = 0
(since TotalCreditCapacityUsd
is negative).
User Withdraws Funds:
Suppose the user withdraws assets worth 500
.
The new TotalCreditCapacityUsd
becomes:
The new TotalCreditCapacityLocked
remains zero (since TotalCreditCapacityUsd
is still negative).
Credit Capacity Delta Check:
The redeem
function checks if the credit capacity delta is greater than the locked credit capacity:
In this case:
The locked credit capacity is zero, so the check:
becomes:
This check fails, and the function does not revert with Errors.NotEnoughUnlockedCreditCapacity()
.
Users can withdraw funds even when the vault is insolvent, leading to liquidity issues or insolvency for connected markets.
The vulnerability was identified through a detailed review of the redeem
function and its interaction with the getLockedCreditCapacityUsd
function.
Add a check to ensure the locked credit capacity is non-zero before allowing withdrawals:
Modify the check to revert when the delta is greater than lockedCreditCapacity
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.