Skip to content

FLO-1: Uncollected Protocol Fees Are Permanently Lost when Reserves Are Low #210

Description

@liobrasil

Severity: High

Files Affected

  • cadence/contracts/FlowALPv1.cdc

Description

The protocol calculates insurance and stability fees as a percentage of the debitIncome within updateInterestRates(). This fee is mathematically deducted from the interest distributed to lenders via the currentCreditRate, implicitly leaving those tokens in the pool's reserves to be claimed by the protocol. However, during collectInsurance() and collectStability(), if the pool's reserveVault lacks sufficient liquidity to cover the accrued fee, the withdrawal is strictly capped at the available balance. The contract then unconditionally updates the lastInsuranceCollectionTime and lastStabilityFeeCollectionTime to the current block timestamp. Because the uncollected remainder is not tracked, it is permanently erased. This not only results in a direct loss of protocol revenue but also corrupts the internal accounting; when borrowers eventually repay loans and replenish the reserves, those uncollected fee tokens become permanently trapped as unallocated surplus. Exploit Scenario:

  • A supported token experiences extremely high utilization, leaving the pool's reserveVault with near-zero available liquidity.
  • The protocol accrues significant debt income over a month, automatically deducting a 5% insurance fee from the lenders' credit rate allocation.
  • A user interaction triggers updateInterestRatesAndCollectInsurance(), which correctly calculates that 1,000 tokens are owed to the insurance fund.
  • Because the reserveVault currently only holds 10 tokens, the collectInsurance() function withdraws the 10 tokens, abandons the remaining 990 tokens, and resets the collection timestamp to the current time.
  • The 990 tokens are permanently lost to the protocol; when borrowers eventually repay their loans and replenish the vault, those 990 tokens will remain trapped in the reserves without belonging to either the protocol or the lenders.

Recommendation

Introduce pendingInsuranceFee and pendingStabilityFee state variables within the TokenState struct to properly track accrued but uncollected fees. During collection, add the newly calculated fee to the pending tracker, attempt to withdraw as much of the pending fee as the reserveVault allows, and strictly subtract only the successfully withdrawn amount from the tracker.


Parent Issue: #209

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions