The DebtToken contract stores balances in scaled form (divided by the index), to get the actual total supply, we need to multiply the scaled supply by the current index, not divide by it. This is evidenced by how balanceOf()
works, which correctly uses rayMul
to get the actual balance:
I have created two test cases where a user borrows 50% of the available liquidity and then I check the accumulated debt after 10 years. While in both cases no other varianrts is changed.
Current Block Number: 21914926
totalLiquidity::1000.0
totalUsage::0.0
liquidityIndex::1000000000000000000000000000
usageIndex::1000000001585489600445117959
userL borrows 50% of totalLiquidity
getUserDebt(userL) 500.0
debtToken.balanceOf(userL) 500.0
10 years later
Current Block Number: 21914931
totalLiquidity::500.0
totalUsage::499.999997621765606874
liquidityIndex::1359374998587923327048812920
usageIndex::2051864857711402869002414542
currentLiquidityRate 35937499858792332704881292
currentUsageRate 71874999888520262557093804
primeRate 100000000000000000000000000
baseRate 25000000000000000000000000
optimalRate 50000000000000000000000000
maxRate 400000000000000000000000000
optimalUtilizationRate 800000000000000000000000000
protocolFeeRate 0
getUserDebt(userL) 1025.932426415793644272
debtToken.balanceOf(userL) 1025.932426415793644272
✔ case1 (2376ms)
skip 10 years, this will increase the usageIndex
Current Block Number: 21914965
totalLiquidity::1000.0
totalUsage::0.0
liquidityIndex::1000000000000000000000000000
usageIndex::1284025419352231513141649859
userL borrows 50% of totalLiquidity
getUserDebt(userL) 500.000000957547527686
debtToken.balanceOf(userL) 500.000000957547527686
10 years later
Current Block Number: 21914970
totalLiquidity::500.0
totalUsage::303.265328597700745876
liquidityIndex::1228013563962445032068870943
usageIndex::2348892928655535385342462509
currentLiquidityRate 22801356307455897162837483
currentUsageRate 60394437608390291641566505
primeRate 100000000000000000000000000
baseRate 25000000000000000000000000
optimalRate 50000000000000000000000000
maxRate 400000000000000000000000000
optimalUtilizationRate 800000000000000000000000000
protocolFeeRate 0
getUserDebt(userL) 914.659824195891311077
debtToken.balanceOf(userL) 914.659824195891311077
✔ case2 (2338ms)
describe("ReserveData.totalUsage", function () {
async function printReserveData() {
const reserve = await lendingPool.reserve();
const blockNumber = await ethers.provider.getBlockNumber();
console.log(`Current Block Number: ${blockNumber}`);
console.log(`totalLiquidity::${ethers.formatEther(reserve[3])}`);
console.log(`totalUsage::${ethers.formatEther(reserve[4])}`);
console.log(`liquidityIndex::${reserve[5]}`);
console.log(`usageIndex::${reserve[6]}`);
console.log("\n");
}
async function printRateData() {
const rates = await lendingPool.rateData();
console.log(`currentLiquidityRate ${rates[0]}`);
console.log(`currentUsageRate ${rates[1]}`);
console.log(`primeRate ${rates[2]}`);
console.log(`baseRate ${rates[3]}`);
console.log(`optimalRate ${rates[4]}`);
console.log(`maxRate ${rates[5]}`);
console.log(`optimalUtilizationRate ${rates[6]}`);
console.log(`protocolFeeRate ${rates[7]}`);
console.log("\n");
}
it("case1", async function() {
const [userL] = await ethers.getSigners();
const myNFT = 146;
const myNFTPrice = ethers.parseEther("1000000000");
await raacHousePrices.setHousePrice(myNFT, myNFTPrice);
await token.connect(owner).mint(userL.address, myNFTPrice);
await token.connect(userL).approve(raacNFT.target, myNFTPrice);
await raacNFT.connect(userL).mint(myNFT, myNFTPrice);
await raacNFT.connect(userL).approve(lendingPool.target, myNFT);
await lendingPool.connect(userL).depositNFT(myNFT);
await printReserveData();
let reserve = await lendingPool.reserve();
const totalLiquidity = reserve[3];
await lendingPool.connect(userL).borrow(totalLiquidity / BigInt(2));
await lendingPool.updateState();
console.log(`userL borrows 50% of totalLiquidity`);
console.log(`getUserDebt(userL) ${ethers.formatEther(await lendingPool.getUserDebt(userL))}`);
console.log(`debtToken.balanceOf(userL) ${ethers.formatEther(await debtToken.balanceOf(userL.address))}`);
await ethers.provider.send("evm_increaseTime", [60 * 60 * 24 * 365 * 10]);
await ethers.provider.send("evm_mine", []);
await lendingPool.updateState();
console.log(`10 years later\n`);
await lendingPool.updateState();
await printReserveData();
await printRateData();
console.log(`getUserDebt(userL) ${ethers.formatEther(await lendingPool.getUserDebt(userL))}`);
console.log(`debtToken.balanceOf(userL) ${ethers.formatEther(await debtToken.balanceOf(userL.address))}`);
});
it("case2", async function() {
await ethers.provider.send("evm_increaseTime", [60 * 60 * 24 * 365 * 10]);
await ethers.provider.send("evm_mine", []);
await lendingPool.updateState();
console.log(`skip 10 years, this will increase the usageIndex`);
const [userL] = await ethers.getSigners();
const myNFT = 146;
const myNFTPrice = ethers.parseEther("1000000000");
await raacHousePrices.setHousePrice(myNFT, myNFTPrice);
await token.connect(owner).mint(userL.address, myNFTPrice);
await token.connect(userL).approve(raacNFT.target, myNFTPrice);
await raacNFT.connect(userL).mint(myNFT, myNFTPrice);
await raacNFT.connect(userL).approve(lendingPool.target, myNFT);
await lendingPool.connect(userL).depositNFT(myNFT);
await printReserveData();
let reserve = await lendingPool.reserve();
const totalLiquidity = reserve[3];
await lendingPool.connect(userL).borrow(totalLiquidity / BigInt(2));
await lendingPool.updateState();
console.log(`userL borrows 50% of totalLiquidity`);
console.log(`getUserDebt(userL) ${ethers.formatEther(await lendingPool.getUserDebt(userL))}`);
console.log(`debtToken.balanceOf(userL) ${ethers.formatEther(await debtToken.balanceOf(userL.address))}`);
await ethers.provider.send("evm_increaseTime", [60 * 60 * 24 * 365 * 10]);
await ethers.provider.send("evm_mine", []);
await lendingPool.updateState();
console.log(`10 years later\n`);
await lendingPool.updateState();
await printReserveData();
await printRateData();
console.log(`getUserDebt(userL) ${ethers.formatEther(await lendingPool.getUserDebt(userL))}`);
console.log(`debtToken.balanceOf(userL) ${ethers.formatEther(await debtToken.balanceOf(userL.address))}`);
});
});