Part 2

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

Unchecked Deposit/Redeem Fees Allow Theft of User Funds in `Vault.sol::create()` and `Vault.sol::update()`

1. Summary

The create() and update() functions lack validation for depositFee and redeemFee, enabling administrators to set fees exceeding 100%. This could result in complete loss of user funds during deposits/redemptions or denial-of-service by making transactions economically impossible.


2. Vulnerability Details

Affected Code

// In create():
function create(CreateParams memory params) internal {
// No fee validation ❌
self.depositFee = params.depositFee;
self.redeemFee = params.redeemFee;
}
// In update():
function update(UpdateParams memory params) internal {
// No fee validation ❌
// (Note: UpdateParams doesn't even include fees in provided code)
}

  1. Fee Representation:

    • Fees use 18-decimal fixed-point (1e18 = 100%)

    • Stored as uint256, allowing values up to 1.15e77 (far beyond 100%)

  2. Exploit Scenarios:

    • Malicious Admin: Sets depositFee = 2e18 (200%) → Users lose all deposited funds + extra

    • Accidental Misconfiguration: Admin typo (e.g., 100000000000000000010000000000000000000) sets 1000% fee

  3. Protocol Impact:

    • Fees applied via amount * fee / 1e18 would invert returns:

      // If fee = 2e18 (200%):
      uint256 feeAmount = depositAmount * 2e18 / 1e18; // feeAmount = 2x deposit!

3. Impact

Severity Consequences
High Direct Fund Theft: Fees >100% let admins siphon user assets

Broken Economics: Fees ≥100% block deposits/redemptions entirely
Reputation Damage: Users lose trust in protocol safety
Governance Attack: Compromised admin keys could drain vaults

Example Attack Flow:

  1. Attacker gains admin privileges (via compromise or malicious proposal)

  2. Sets redeemFee = 1.5e18 (150%)

  3. Users attempting to redeem $100 receive:

    $100 - ($100 * 150%) = -$50 → Protocol owes users money (impossible, reverts)

    Result: All redemptions fail, funds permanently locked.


4. Recommendations

Immediate Fix

Add explicit fee validation in both functions:

// In create():
function create(CreateParams memory params) internal {
if (params.depositFee > 1e18 || params.redeemFee > 1e18) {
revert Errors.InvalidFee();
}
// ... rest of logic ...
}
// In update() (if fee updates are allowed):
function update(UpdateParams memory params) internal {
if (params.depositFee > 1e18 || params.redeemFee > 1e18) {
revert Errors.InvalidFee();
}
// ... rest of logic ...
}

Enhanced Protections

  1. Event Emission:
    Log fee changes for transparency:

    event FeesUpdated(uint128 vaultId, uint256 depositFee, uint256 redeemFee);
  2. Input Sanitization:
    Reject fees exceeding a safe threshold (e.g., 20%):

    uint256 constant MAX_FEE = 0.2e18; // 20%
    if (params.depositFee > MAX_FEE || params.redeemFee > MAX_FEE) revert;
  3. Multi-Sig Enforcement:
    Require multiple signers for fee changes above 5%.

  4. Timelock:
    Implement a 24-72 hour delay for fee adjustments to allow user exits.

Updates

Lead Judging Commences

inallhonesty Lead Judge
7 months ago
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.