sherlock-audit / 2024-08-saffron-finance-judging

9 stars 5 forks source link

0x73696d616f - Exit fees are not quadratically scalled as they should be which will allow fixed users to withdraw for less fees early on #126

Open sherlock-admin2 opened 1 month ago

sherlock-admin2 commented 1 month ago

0x73696d616f

High

Exit fees are not quadratically scalled as they should be which will allow fixed users to withdraw for less fees early on

Summary

The codebase states in several locations that the earlyExitFeeBps is quadratically scaled, but it is not. VaultFactory

Fixed income early exit fee in basis points (one basis point = 1/100 of 1%) that is quadratically scaled based off of early exit time

LidoVault

/// @notice Penalty fee in basis points for fixed side early withdrawals that is quadratically scaled based off of the amount of time that has elapsed since the vault started

However, if we look at LidoVault::calculateFixedEarlyExitFees(), it is the sum of 2 linear functions, one going from upfrontPremium * earlyExitFeeBps to 0 and another one from upfrontPremium to 0. This results in a linear function going from upfrontPremium * (1 + earlyExitFeeBps) to 0, which is not quadratic.

Thus, users can withdraw earlier with less fees as the quadratic would penalize much more users withdrawing early in the duration of the vault.

Root Cause

In LidoVault:982, it does not calculate an early exit fee quadratically.

Internal pre-conditions

None.

External pre-conditions

None.

Attack Path

  1. Fixed user does an early withdraw but pays less fees than supposed or more fees if near the end.

Impact

Variable users take losses if the fixed user is withdrawing early in the vault's duration or the fixed user takes a loss if withdrawing late.

PoC

The LidoVault::calculateFixedEarlyExitFees() function calculates fees based on a linear relationship with the duration left.

function calculateFixedEarlyExitFees(uint256 upfrontPremium, uint256 timestampRequested)
    internal
    view
    returns (uint256)
{
    uint256 remainingProportion =
        (endTime > timestampRequested ? endTime - timestampRequested : 0).mulDiv(1e18, duration);

    // Calculate the scaling fee based on the linear factor and earlyExitFeeBps
    uint256 earlyExitFees = upfrontPremium.mulDiv((1 + earlyExitFeeBps).mulDiv(remainingProportion, 1e18), 10000);

    // Calculate the amount to be paid back of their original upfront claimed premium, not influenced by quadratic scaling
    earlyExitFees += upfrontPremium - upfrontPremium.mulDiv(timestampRequested - startTime, duration);

    return earlyExitFees;
}

Mitigation

Implement the quadratic function as intended.