Part 2

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

Infinite ERC20 Token Approval to MarketMakingEngine Creates Systemic Risk of Fund Drainage

## Summary
A critical vulnerability exists in the ZlpVault due to an infinite ERC20 token approval granted to the MarketMakingEngine. This creates a systemic risk where a compromise of the engine (e.g., via exploit, upgrade, or misconfiguration) could result in the total drainage of all assets held by the vault.


## Vulnerability Details
Code Snippet (Initialize Function):

function initialize(...) external initializer {
...
IERC20(asset_).approve(marketMakingEngine, type(uint256).max); // 🚨 Infinite approval
}
  • Problem: The vault grants type(uint256).max (infinite) approval to the MarketMakingEngine during initialization.

  • Mechanism: The MarketMakingEngine can call transferFrom on the vault’s asset at any time, bypassing all access controls in the ZlpVault itself.

  • Attack Path: If the engine is compromised (e.g., flawed logic, malicious upgrade, or private key leak), an attacker can directly drain all assets from the vault.


## Impact

  • Critical Severity: All funds in the vault are at immediate risk.

  • Permanent Risk: Even if the engine is currently secure, future upgrades or governance decisions could introduce vulnerabilities.

  • Real-World Analogy: Similar to the Poly Network hack, where excessive approvals led to $600M stolen.


## Tools Used

  1. Manual Code Review: Identified the infinite approval in initialize().

  2. Slither: Flagged approve with type(uint256).max as high-risk.

  3. Ethereum Execution Traces: Simulated a scenario where MarketMakingEngine calls transferFrom to drain assets.


## Recommendations
1. Remove Infinite Approval:

// Initialize function: Remove the approve call entirely
// IERC20(asset_).approve(marketMakingEngine, type(uint256).max); ❌ Delete this line

2. Use Pull-Based Allowances:
Before interactions with the engine (e.g., deposits/withdrawals), approve only the required amount and reset to 0 afterward:

function deposit(uint256 assets, address receiver) public override ... {
IERC20(asset()).approve(marketMakingEngine, assets); // Approve exact amount
super.deposit(assets, receiver);
IERC20(asset()).approve(marketMakingEngine, 0); // Reset allowance
}

3. Emergency Mitigation:

  • Revoke the current infinite approval immediately via approve(marketMakingEngine, 0).

  • Audit all existing MarketMakingEngine code paths that interact with the vault’s asset.

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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