Skip to content
Request an audit

Borrow fee uses APY as per-second rate, causing extreme overcharging

TitleBorrow fee uses APY as per-second rate, causing extreme overcharging
Reward$1196, 3 dups
ContestAmmplify - 2 Sep 2025 on Sherlock
Authorblockace
ContextAPY logic

The borrow fee rate returned by the smooth rate curve is an annual percentage yield (APY), but the implementation multiplies it directly by elapsed seconds without annualizing. This treats an annual rate as a per-second rate, inflating fees by roughly $31{,}536{,}000\times$ the intended amount. Borrowers are charged massive, unjustified fees even over short time intervals.

The function chargeTrueFeeRate in src/walkers/Fee.sol treats the APY returned by calculateRateX64 as a per-second rate, multiplying it by the elapsed seconds timeDiff without dividing by the number of seconds in a year. The rate returned by the curve is annualised. The library documents a seconds-per-year factor of 31,536,000 and distinguishes APR from SPR:

solidity
// src/walkers/Fee.sol
// ... inside FeeWalker.chargeTrueFeeRate( ... )
uint256 timeDiff = uint128(block.timestamp) - data.timestamp; // Convert to 256 for next mult
uint256 takerRateX64 = timeDiff * data.fees.rateConfig.calculateRateX64(
    uint64((totalTLiq << 64) / totalMLiq)
);

// lib/Commons/src/Math/SmoothRateCurveLib.sol
// ...
// SPR factor 31536000 = 365 * 24 * 60 * 60
// APR of 0.001% = 0.00001
// as a SPR = 0.00001 / 31536000 =  ...

The correct computation must divide by 365 days to convert the annual rate into a per-second rate before applying timeDiff.

Alpha: the code would have been correct but for the comment stating what the variable holds — the return value could have been a per-second rate. Read those comments carefully.

Conclusion

This finding would earn you $4432, and it is genuinely tricky: you have to establish whether calculateRateX64 returns a per-second or an annual rate.

Full Report
Codebase

Message on Telegram All 62 posts