Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: high
Invalid

Missing Enforcement of Deposit Cap in ZlpVault.deposit()

Summary and Impact

The vulnerability arises because the ZlpVault contract provides an advisory deposit cap via its maxDeposit function but fails to enforce this limit in its deposit(...) implementation. In essence, while the vault is designed to restrict total deposits (e.g., a cap of 1,000 USDC), the overridden deposit function simply delegates to ERC4626’s implementation without checking whether the incoming deposit exceeds the computed maximum (i.e. depositCap – totalAssets()). This oversight breaks a core invariant of the vault’s risk management model. Without enforcement, a user (or malicious actor) can deposit an arbitrarily large amount, causing the vault to hold assets far beyond its intended safe operating range. This can compromise liquidity controls, risk assessments, and overall protocol stability.


Vulnerability Details

Technical Explanation:

The ZlpVault contract is expected to maintain a deposit cap to prevent overexposure. Its advisory function calculates:

function maxDeposit(address) public view override returns (uint256 maxAssets) {
// maxAssets = depositCap - totalAssets
}

However, the actual deposit function is implemented as follows:

function deposit(uint256 assets, address receiver) public override onlyMarketMakingEngine returns (uint256) {
return super.deposit(assets, receiver);
}

Notice that there is no check ensuring that assets <= maxDeposit(receiver). Thus, even if maxDeposit() returns a value far below the proposed deposit, the function will accept an arbitrarily large amount.

Test Code Snippet (Conceptual):

// Assume: depositCap = 1,000 tokens and current totalAssets() = 900 tokens.
// Then maxDeposit() returns 100 tokens.
// Exploit scenario:
uint256 depositAmount = 5_000 * 1e18; // 5,000 tokens, far exceeding the 100-token advisory cap.
// Called from an authorized market-making engine:
zlpVault.deposit(depositAmount, attacker);
// Expected outcome:
// totalAssets() becomes 900 + 5,000 = 5,900 tokens,
// violating the intended deposit cap and undermining risk controls.

This snippet demonstrates that the vault accepts deposits that far exceed the intended limit, thereby breaching the protocol’s risk assumptions and potentially leading to liquidity and exposure issues.

Why This Is a Vulnerability:

  • Invariant Violation: The system’s risk management relies on deposit caps to limit exposure. Allowing deposits beyond the cap directly contravenes this invariant.

  • Systemic Impact: Unchecked deposits can skew internal metrics, misalign incentives, and expose the protocol to unforeseen liquidity risks.

  • Economic Exploitation: Overlarge deposits may be used to manipulate the vault’s state or trigger other miscalculations in the protocol’s financial mechanisms.


Tools Used

  • Manual Review

  • Foundry


Recommendations

To mitigate this vulnerability, enforce the deposit cap within the deposit(...) function. A recommended fix is to add a require check that ensures the deposit does not exceed the maximum allowed amount, for example:

function deposit(uint256 assets, address receiver)
public
override
onlyMarketMakingEngine
returns (uint256)
{
require(assets <= maxDeposit(receiver), "DepositCapExceeded");
return super.deposit(assets, receiver);
}

Updates

Lead Judging Commences

inallhonesty Lead Judge
7 months ago
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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