When bad debt accumulates, it should be socialized among all suppliers to distribute the loss fairly.
This means that even if bad debt accrues, the first users to withdraw will be able to withdraw their shares to assets at a good rate, leaving the last users with all the loss.
import { expect, use } from 'chai';
import hre from "hardhat";
const { ethers } = hre;
describe("RAAC PoC", function() {
let owner, user1, user2, user3;
let crvusd, raacNFT, raacHousePrices;
let lendingPool, rToken, debtToken;
let token;
beforeEach(async function () {
[owner, user1, user2, user3] = await ethers.getSigners();
const CrvUSDToken = await ethers.getContractFactory("crvUSDToken");
crvusd = await CrvUSDToken.deploy(owner.address);
await crvusd.setMinter(owner.address);
token = crvusd;
const RAACHousePrices = await ethers.getContractFactory("RAACHousePrices");
raacHousePrices = await RAACHousePrices.deploy(owner.address);
const RAACNFT = await ethers.getContractFactory("RAACNFT");
raacNFT = await RAACNFT.deploy(crvusd.target, raacHousePrices.target, owner.address);
const RToken = await ethers.getContractFactory("RToken");
rToken = await RToken.deploy("RToken", "RToken", owner.address, crvusd.target);
const DebtToken = await ethers.getContractFactory("DebtToken");
debtToken = await DebtToken.deploy("DebtToken", "DT", owner.address);
const RAACToken = await ethers.getContractFactory("RAACToken");
const raacToken = await RAACToken.deploy(owner.address, 100, 50);
const DEToken = await ethers.getContractFactory("DEToken");
const deToken = await DEToken.deploy("DEToken", "DEToken", owner.address, rToken.target);
const initialPrimeRate = ethers.parseUnits("0.1", 27);
const LendingPool = await ethers.getContractFactory("LendingPool");
lendingPool = await LendingPool.deploy(
crvusd.target,
rToken.target,
debtToken.target,
raacNFT.target,
raacHousePrices.target,
initialPrimeRate
);
await lendingPool.setStabilityPool(owner.address);
await rToken.setReservePool(lendingPool.target);
await debtToken.setReservePool(lendingPool.target);
await rToken.transferOwnership(lendingPool.target);
await debtToken.transferOwnership(lendingPool.target);
const mintAmount = ethers.parseEther("1000");
await crvusd.mint(user1.address, mintAmount);
await crvusd.mint(user2.address, mintAmount);
await crvusd.mint(user3.address, mintAmount);
await crvusd.mint(owner.address, ethers.parseEther("800"));
await crvusd.connect(user1).approve(lendingPool.target, mintAmount);
await crvusd.connect(user2).approve(lendingPool.target, mintAmount);
await crvusd.connect(user3).approve(lendingPool.target, mintAmount);
await raacHousePrices.setOracle(owner.address);
await raacHousePrices.setHousePrice(1, ethers.parseEther("500"));
await raacHousePrices.setHousePrice(2, ethers.parseEther("500"));
await ethers.provider.send("evm_mine", []);
const tokenId = 1;
const amountToPay1 = ethers.parseEther("500");
const amountToPay2 = ethers.parseEther("500");
await token.mint(user1.address, amountToPay1 + amountToPay2);
await token.connect(user1).approve(raacNFT.target, amountToPay1 + amountToPay2);
await raacNFT.connect(user1).mint(tokenId, amountToPay1);
await raacNFT.connect(user1).mint(tokenId + 1, amountToPay2);
const depositAmount = ethers.parseEther("1000");
await crvusd.connect(user2).approve(lendingPool.target, depositAmount);
await lendingPool.connect(user2).deposit(depositAmount);
await crvusd.connect(user3).approve(lendingPool.target, depositAmount);
await lendingPool.connect(user3).deposit(depositAmount);
await ethers.provider.send("evm_mine", []);
expect(await crvusd.balanceOf(rToken.target)).to.equal(ethers.parseEther("2000"));
});
it("does not distribute the loss among all suppliers during bad debt", async () => {
const tokenId1 = 1;
const tokenId2 = 2;
await raacNFT.connect(user1).approve(lendingPool.target, tokenId1);
await lendingPool.connect(user1).depositNFT(tokenId1);
await raacNFT.connect(user1).approve(lendingPool.target, tokenId2);
await lendingPool.connect(user1).depositNFT(tokenId2);
await lendingPool.connect(user1).borrow(ethers.parseEther("800"));
await raacHousePrices.setHousePrice(1, ethers.parseEther("300"));
await raacHousePrices.setHousePrice(2, ethers.parseEther("300"));
await lendingPool.initiateLiquidation(user1.address);
await ethers.provider.send("evm_increaseTime", [72 * 60 * 60 + 1]);
await ethers.provider.send("evm_mine");
await expect(lendingPool.connect(owner).finalizeLiquidation(user1.address))
.to.be.revertedWithCustomError(crvusd, "ERC20InsufficientAllowance");
await lendingPool.connect(user2).withdraw(ethers.parseEther("1000"));
await expect(lendingPool.connect(user3).withdraw(ethers.parseEther("1000")))
.to.be.revertedWithCustomError(crvusd, "ERC20InsufficientBalance");
console.log("User1 balance: ", await crvusd.balanceOf(user2));
console.log("user2 balance: ", await crvusd.balanceOf(user3));
});
});