In the DebtToken contract, the totalSupply() function incorrectly uses rayDiv instead of rayMul when scaling the total supply with the normalized debt index, resulting in LendingPool accounting such as calculation of interest rates to be wrong.
The DebtToken contract implements a debt accounting system where token balances are scaled by a usage index (normalized debt) to account for accumulated interest over time. This follows the same pattern as Aave's variable debt tokens.
The current implementation of totalSupply() is:
This is incorrect because:
The normalized debt index (_usageIndex) starts at RAY (1e27) and increases over time to reflect accumulated interest
When calculating debt balances, we need to multiply by the index to get the current debt amount including interest
Using rayDiv instead of rayMul results in dividing by the index, which:
Decreases the total supply when it should increase
Returns an incorrect value that's inverse to the actual total debt
Creates inconsistency with individual balance calculations
This is incosistent with the balanceOf() function which correctly uses rayMul:
The discrepancy between these two functions creates a situation where the sum of individual balances doesn't match the total supply, breaking a fundamental invariant of ERC20 tokens and the audit scope did say the tokens should be 100% EIP20 compliant.
Furthermore, in both the burn and mint functions of the DebtToken, the totalSupply is always returned as one of the return parameters;
In the LendingPool contract, the _repay, borrow, and finalizeLiquidation functions all call either the burn or mint function on the DebtToken and the returned newTotalSupply is used to updated reserve's totalUsage.
For instance in the LendingPool's borrow function;
Looking at ReserveLibrary.updateInterestRatesAndLiquidity;
From above analysis, we have done we can conclude that this issue causes the protocol to always use incorrect borrow rates as well as incorrect interest rates.
The PoC below just focuses on breaking the ERC20 compliance, but I hope the explanation for how the issue affects the interest and borrow rates has been clarified above.
User A borrows 100 tokens when usage index = RAY (1e27)
Time passes and usage index increases to 1.1 * RAY
balanceOf(A) correctly returns 110 tokens (100 * 1.1)
But totalSupply() incorrectly returns ~90.9 tokens (100 / 1.1)
This creates an accounting discrepancy where total supply is less than individual balance
Incorrect borrow and interest rates negatively affect protocol's economics which leads to protocol's insolvency.
Incorrect reporting of total protocol debt
Broken ERC20 invariant where sum of balances ≠ total supply
Manual code review
Change the totalSupply() function to use rayMul:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.