const { expect } = require("chai");
const { ethers, network } = require("hardhat");
describe("DebtToken Vulnerability PoC", function () {
const RAY = ethers.BigNumber.from("1000000000000000000000000000");
const INCREASED_INDEX = ethers.BigNumber.from("1100000000000000000000000000");
const BORROW_AMOUNT = ethers.BigNumber.from("100000000000000000000");
let owner, user;
let dummyLendingPool, debtToken;
before(async function () {
[owner, user] = await ethers.getSigners();
const DummyLendingPool = await ethers.getContractFactory("DummyLendingPool");
dummyLendingPool = await DummyLendingPool.deploy();
await dummyLendingPool.deployed();
const DebtToken = await ethers.getContractFactory("DebtToken");
debtToken = await DebtToken.deploy("Debt Token", "DBT", owner.address);
await debtToken.deployed();
await debtToken.setReservePool(dummyLendingPool.address);
await network.provider.send("hardhat_setBalance", [
dummyLendingPool.address,
"0x3635C9ADC5DEA00000",
]);
});
it("demonstrates over-mint vulnerability", async function () {
await dummyLendingPool.setNormalizedDebt(RAY);
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [dummyLendingPool.address],
});
const reserveSigner = await ethers.getSigner(dummyLendingPool.address);
let tx1 = await debtToken.connect(reserveSigner).mint(
user.address,
user.address,
BORROW_AMOUNT,
RAY
);
let receipt1 = await tx1.wait();
const supplyAfterFirst = await debtToken.totalSupply();
await dummyLendingPool.setNormalizedDebt(INCREASED_INDEX);
let tx2 = await debtToken.connect(reserveSigner).mint(
user.address,
user.address,
BORROW_AMOUNT,
INCREASED_INDEX
);
let receipt2 = await tx2.wait();
const supplyAfterSecond = await debtToken.totalSupply();
console.log("Supply after first mint:", supplyAfterFirst.toString());
console.log("Supply after second mint:", supplyAfterSecond.toString());
await network.provider.request({
method: "hardhat_stopImpersonatingAccount",
params: [dummyLendingPool.address],
});
});
});
function mint(...) external override onlyReservePool returns (...) {
uint256 amountScaled = amount.rayDiv(index);
if (amountScaled == 0) revert InvalidAmount();
uint256 previousScaledBalance = super.balanceOf(onBehalfOf);
bool isFirstMint = previousScaledBalance == 0;
uint256 balanceIncrease = previousScaledBalance.rayMul(index - _userState[onBehalfOf].index);
_userState[onBehalfOf].index = index.toUint128();
_mint(onBehalfOf, amountScaled);
emit Transfer(address(0), onBehalfOf, amountScaled);
emit Mint(user, onBehalfOf, amount, balanceIncrease, index);
return (isFirstMint, amountScaled, totalSupply());
}