Summary
The DebtToken::totalSupply()
function incorrectly calculates the total supply due to the use of an incorrect mathematical operation. This results in a return value that does not accurately reflect the total supply.
Vulnerability Details
As indicated by the @>
marker, the function incorrectly applies rayDiv
instead of rayMul
, leading to an incorrect computation of the total supply.
* @notice Returns the scaled total supply
* @return The total supply (scaled by the usage index)
*/
function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledSupply = super.totalSupply();
@> return scaledSupply.rayDiv(ILendingPool(_reservePool).getNormalizedDebt());
}
Poc
Add the following test case to test/unit/core/pools/LendingPool/LendingPool.test.js
and execute it:
describe("DebtToken totalSupply()", function () {
beforeEach("Simulate real-world interest rates", async function () {
const depositAmount = ethers.parseEther("1000");
await crvusd.connect(user2).approve(lendingPool.target, depositAmount);
await lendingPool.connect(user2).deposit(depositAmount);
const tokenId = 1;
await raacNFT.connect(user1).approve(lendingPool.target, tokenId);
await lendingPool.connect(user1).depositNFT(tokenId);
const borrowAmount = ethers.parseEther("20");
await lendingPool.connect(user1).borrow(borrowAmount);
await lendingPool.connect(user1).updateState();
await ethers.provider.send("evm_increaseTime", [365 * 24 * 60 * 60]);
await ethers.provider.send("evm_mine", []);
await lendingPool.connect(user1).updateState();
});
it("output:", async function () {
console.log("balanceOf(user1.address):", await debtToken.balanceOf(user1.address));
console.log("totalSupply()", await debtToken.totalSupply());
console.log("scaledBalanceOf(user1.address):", await debtToken.scaledBalanceOf(user1.address));
console.log("scaledTotalSupply():",await debtToken.scaledTotalSupply());
});
});
output:
LendingPool
DebtToken totalSupply()
Promise { <pending> }
balanceOf(user1.address): 20525536116956021902n
totalSupply() 19487919531825889295n
scaledBalanceOf(user1.address): 19999999904870624275n
scaledTotalSupply(): 19999999904870624275n
✔ output:
Impact
Due to the incorrect mathematical operation (rayDiv
instead of rayMul
), the totalSupply()
function returns an inaccurate value. This can lead to inconsistencies in lending pool calculations, affecting the accuracy of debt-related metrics.
Tools Used
Manual Review
Recommendations
Modify the function to use rayMul
instead of rayDiv
:
function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledSupply = super.totalSupply();
- return scaledSupply.rayDiv(ILendingPool(_reservePool).getNormalizedDebt());
+ return scaledSupply.rayMulILendingPool(_reservePool).getNormalizedDebt());
}
test again:
LendingPool
DebtToken totalSupply()
Promise { <pending> }
balanceOf(user1.address): 20525536117137245878n
totalSupply() 20525536117137245878n
scaledBalanceOf(user1.address): 19999999904870624275n
scaledTotalSupply(): 19999999904870624275n