From 46856ac704e8178dc01dbc34478e1d50012a8716 Mon Sep 17 00:00:00 2001 From: RickGriff Date: Tue, 30 Jan 2024 11:14:29 +0700 Subject: [PATCH] Fix StabilityPool tests --- contracts/test/StabilityPoolTest.js | 6701 +++++++++------------------ 1 file changed, 2137 insertions(+), 4564 deletions(-) diff --git a/contracts/test/StabilityPoolTest.js b/contracts/test/StabilityPoolTest.js index 26d0f42f..9219a7e9 100644 --- a/contracts/test/StabilityPoolTest.js +++ b/contracts/test/StabilityPoolTest.js @@ -56,13 +56,11 @@ contract("StabilityPool", async (accounts) => { let stabilityPool; let defaultPool; let borrowerOperations; - let lqtyToken; - let communityIssuance; let gasPriceInWei; - const getOpenTroveLUSDAmount = async (totalDebt) => - th.getOpenTroveLUSDAmount(contracts, totalDebt); + const getOpenTroveBoldAmount = async (totalDebt) => + th.getOpenTroveBoldAmount(contracts, totalDebt); const openTrove = async (params) => th.openTrove(contracts, params); const assertRevert = th.assertRevert; @@ -79,11 +77,6 @@ contract("StabilityPool", async (accounts) => { contracts.stabilityPool.address, contracts.borrowerOperations.address ); - const LQTYContracts = await deploymentHelper.deployLQTYContracts( - bountyAddress, - lpRewardsAddress, - multisig - ); priceFeed = contracts.priceFeedTestnet; boldToken = contracts.boldToken; @@ -95,25 +88,15 @@ contract("StabilityPool", async (accounts) => { borrowerOperations = contracts.borrowerOperations; hintHelpers = contracts.hintHelpers; - lqtyToken = LQTYContracts.lqtyToken; - communityIssuance = LQTYContracts.communityIssuance; - - await deploymentHelper.connectCoreContracts(contracts, LQTYContracts); - await deploymentHelper.connectLQTYContractsToCore( - LQTYContracts, - contracts - ); - - // Register 3 front ends - await th.registerFrontEnds(frontEnds, stabilityPool); + await deploymentHelper.connectCoreContracts(contracts); }); // --- provideToSP() --- - // increases recorded LUSD at Stability Pool - it("provideToSP(): increases the Stability Pool LUSD balance", async () => { + // increases recorded Bold at Stability Pool + it("provideToSP(): increases the Stability Pool Bold balance", async () => { // --- SETUP --- Give Alice a least 200 await openTrove({ - extraLUSDAmount: toBN(200), + extraBoldAmount: toBN(200), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); @@ -121,18 +104,18 @@ contract("StabilityPool", async (accounts) => { // --- TEST --- // provideToSP() - await stabilityPool.provideToSP(200, ZERO_ADDRESS, { from: alice }); + await stabilityPool.provideToSP(200, { from: alice }); - // check LUSD balances after - const stabilityPool_LUSD_After = - await stabilityPool.getTotalLUSDDeposits(); - assert.equal(stabilityPool_LUSD_After, 200); + // check Bold balances after + const stabilityPool_Bold_After = + await stabilityPool.getTotalBoldDeposits(); + assert.equal(stabilityPool_Bold_After, 200); }); it("provideToSP(): updates the user's deposit record in StabilityPool", async () => { // --- SETUP --- Give Alice a least 200 await openTrove({ - extraLUSDAmount: toBN(200), + extraBoldAmount: toBN(200), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); @@ -140,56 +123,56 @@ contract("StabilityPool", async (accounts) => { // --- TEST --- // check user's deposit record before const alice_depositRecord_Before = await stabilityPool.deposits(alice); - assert.equal(alice_depositRecord_Before[0], 0); + assert.equal(alice_depositRecord_Before, 0); // provideToSP() - await stabilityPool.provideToSP(200, frontEnd_1, { from: alice }); + await stabilityPool.provideToSP(200, { from: alice }); // check user's deposit record after const alice_depositRecord_After = ( await stabilityPool.deposits(alice) - )[0]; + ); assert.equal(alice_depositRecord_After, 200); }); - it("provideToSP(): reduces the user's LUSD balance by the correct amount", async () => { + it("provideToSP(): reduces the user's Bold balance by the correct amount", async () => { // --- SETUP --- Give Alice a least 200 await openTrove({ - extraLUSDAmount: toBN(200), + extraBoldAmount: toBN(200), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); // --- TEST --- // get user's deposit record before - const alice_LUSDBalance_Before = await boldToken.balanceOf(alice); + const alice_BoldBalance_Before = await boldToken.balanceOf(alice); // provideToSP() - await stabilityPool.provideToSP(200, frontEnd_1, { from: alice }); + await stabilityPool.provideToSP(200, { from: alice }); - // check user's LUSD balance change - const alice_LUSDBalance_After = await boldToken.balanceOf(alice); + // check user's Bold balance change + const alice_BoldBalance_After = await boldToken.balanceOf(alice); assert.equal( - alice_LUSDBalance_Before.sub(alice_LUSDBalance_After), + alice_BoldBalance_Before.sub(alice_BoldBalance_After), "200" ); }); - it("provideToSP(): increases totalLUSDDeposits by correct amount", async () => { + it("provideToSP(): increases totalBoldDeposits by correct amount", async () => { // --- SETUP --- - // Whale opens Trove with 50 ETH, adds 2000 LUSD to StabilityPool + // Whale opens Trove with 50 ETH, adds 2000 Bold to StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale }, }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(2000, 18), { from: whale, }); - const totalLUSDDeposits = await stabilityPool.getTotalLUSDDeposits(); - assert.equal(totalLUSDDeposits, dec(2000, 18)); + const totalBoldDeposits = await stabilityPool.getTotalBoldDeposits(); + assert.equal(totalBoldDeposits, dec(2000, 18)); }); it("provideToSP(): Correctly updates user snapshots of accumulated rewards per unit staked", async () => { @@ -197,28 +180,28 @@ contract("StabilityPool", async (accounts) => { // Whale opens Trove and deposits to SP await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); - const whaleLUSD = await boldToken.balanceOf(whale); - await stabilityPool.provideToSP(whaleLUSD, frontEnd_1, { from: whale }); + const whaleBold = await boldToken.balanceOf(whale); + await stabilityPool.provideToSP(whaleBold, { from: whale }); // 2 Troves opened, each withdraws minimum debt await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_2 }, }); - // Alice makes Trove and withdraws 100 LUSD + // Alice makes Trove and withdraws 100 Bold await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), + extraBoldAmount: toBN(dec(100, 18)), ICR: toBN(dec(5, 18)), extraParams: { from: alice, value: dec(50, "ether") }, }); @@ -226,7 +209,7 @@ contract("StabilityPool", async (accounts) => { // price drops: defaulter's Troves fall below MCR, whale doesn't await priceFeed.setPrice(dec(105, 18)); - const SPLUSD_Before = await stabilityPool.getTotalLUSDDeposits(); + const SPBold_Before = await stabilityPool.getTotalBoldDeposits(); // Troves are closed await troveManager.liquidate(defaulter_1, { from: owner }); @@ -235,13 +218,12 @@ contract("StabilityPool", async (accounts) => { assert.isFalse(await sortedTroves.contains(defaulter_2)); // Confirm SP has decreased - const SPLUSD_After = await stabilityPool.getTotalLUSDDeposits(); - assert.isTrue(SPLUSD_After.lt(SPLUSD_Before)); + const SPBold_After = await stabilityPool.getTotalBoldDeposits(); + assert.isTrue(SPBold_After.lt(SPBold_Before)); // --- TEST --- const P_Before = await stabilityPool.P(); const S_Before = await stabilityPool.epochToScaleToSum(0, 0); - const G_Before = await stabilityPool.epochToScaleToG(0, 0); assert.isTrue(P_Before.gt(toBN("0"))); assert.isTrue(S_Before.gt(toBN("0"))); @@ -249,13 +231,11 @@ contract("StabilityPool", async (accounts) => { const alice_snapshot_Before = await stabilityPool.depositSnapshots(alice); const alice_snapshot_S_Before = alice_snapshot_Before[0].toString(); const alice_snapshot_P_Before = alice_snapshot_Before[1].toString(); - const alice_snapshot_G_Before = alice_snapshot_Before[2].toString(); assert.equal(alice_snapshot_S_Before, "0"); assert.equal(alice_snapshot_P_Before, "0"); - assert.equal(alice_snapshot_G_Before, "0"); // Make deposit - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(100, 18), { from: alice, }); @@ -263,50 +243,48 @@ contract("StabilityPool", async (accounts) => { const alice_snapshot_After = await stabilityPool.depositSnapshots(alice); const alice_snapshot_S_After = alice_snapshot_After[0].toString(); const alice_snapshot_P_After = alice_snapshot_After[1].toString(); - const alice_snapshot_G_After = alice_snapshot_After[2].toString(); assert.equal(alice_snapshot_S_After, S_Before); assert.equal(alice_snapshot_P_After, P_Before); - assert.equal(alice_snapshot_G_After, G_Before); }); it("provideToSP(), multiple deposits: updates user's deposit and snapshots", async () => { // --- SETUP --- // Whale opens Trove and deposits to SP await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); - const whaleLUSD = await boldToken.balanceOf(whale); - await stabilityPool.provideToSP(whaleLUSD, frontEnd_1, { from: whale }); + const whaleBold = await boldToken.balanceOf(whale); + await stabilityPool.provideToSP(whaleBold, { from: whale }); - // 3 Troves opened. Two users withdraw 160 LUSD each + // 3 Troves opened. Two users withdraw 160 Bold each await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1, value: dec(50, "ether") }, }); await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_2, value: dec(50, "ether") }, }); await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_3, value: dec(50, "ether") }, }); // --- TEST --- - // Alice makes deposit #1: 150 LUSD + // Alice makes deposit #1: 150 Bold await openTrove({ - extraLUSDAmount: toBN(dec(250, 18)), + extraBoldAmount: toBN(dec(250, 18)), ICR: toBN(dec(3, 18)), extraParams: { from: alice }, }); - await stabilityPool.provideToSP(dec(150, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(150, 18), { from: alice, }); @@ -319,22 +297,22 @@ contract("StabilityPool", async (accounts) => { // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active await priceFeed.setPrice(dec(105, 18)); - // 2 users with Trove with 180 LUSD drawn are closed - await troveManager.liquidate(defaulter_1, { from: owner }); // 180 LUSD closed - await troveManager.liquidate(defaulter_2, { from: owner }); // 180 LUSD closed + // 2 users with Trove with 180 Bold drawn are closed + await troveManager.liquidate(defaulter_1, { from: owner }); // 180 Bold closed + await troveManager.liquidate(defaulter_2, { from: owner }); // 180 Bold closed const alice_compoundedDeposit_1 = - await stabilityPool.getCompoundedLUSDDeposit(alice); + await stabilityPool.getCompoundedBoldDeposit(alice); // Alice makes deposit #2 const alice_topUp_1 = toBN(dec(100, 18)); - await stabilityPool.provideToSP(alice_topUp_1, frontEnd_1, { + await stabilityPool.provideToSP(alice_topUp_1, { from: alice, }); const alice_newDeposit_1 = ( await stabilityPool.deposits(alice) - )[0].toString(); + ).toString(); assert.equal( alice_compoundedDeposit_1.add(alice_topUp_1), alice_newDeposit_1 @@ -353,13 +331,13 @@ contract("StabilityPool", async (accounts) => { assert.isTrue(alice_Snapshot_S_1.eq(S_1)); assert.isTrue(alice_Snapshot_P_1.eq(P_1)); - // Bob withdraws LUSD and deposits to StabilityPool + // Bob withdraws Bold and deposits to StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob }, }); - await stabilityPool.provideToSP(dec(427, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(427, 18), { from: alice, }); @@ -367,15 +345,15 @@ contract("StabilityPool", async (accounts) => { await troveManager.liquidate(defaulter_3, { from: owner }); const alice_compoundedDeposit_2 = - await stabilityPool.getCompoundedLUSDDeposit(alice); + await stabilityPool.getCompoundedBoldDeposit(alice); const P_2 = await stabilityPool.P(); const S_2 = await stabilityPool.epochToScaleToSum(0, 0); assert.isTrue(P_2.lt(P_1)); assert.isTrue(S_2.gt(S_1)); - // Alice makes deposit #3: 100LUSD - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { + // Alice makes deposit #3: 100Bold + await stabilityPool.provideToSP(dec(100, 18), { from: alice, }); @@ -387,31 +365,30 @@ contract("StabilityPool", async (accounts) => { assert.isTrue(alice_Snapshot_P_2.eq(P_2)); }); - it("provideToSP(): reverts if user tries to provide more than their LUSD balance", async () => { + it("provideToSP(): reverts if user tries to provide more than their Bold balance", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice, value: dec(50, "ether") }, }); await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob, value: dec(50, "ether") }, }); - const aliceLUSDbal = await boldToken.balanceOf(alice); - const bobLUSDbal = await boldToken.balanceOf(bob); + const aliceBoldbal = await boldToken.balanceOf(alice); + const bobBoldbal = await boldToken.balanceOf(bob); // Alice, attempts to deposit 1 wei more than her balance const aliceTxPromise = stabilityPool.provideToSP( - aliceLUSDbal.add(toBN(1)), - frontEnd_1, + aliceBoldbal.add(toBN(1)), { from: alice } ); await assertRevert(aliceTxPromise, "revert"); @@ -419,26 +396,25 @@ contract("StabilityPool", async (accounts) => { // Bob, attempts to deposit 235534 more than his balance const bobTxPromise = stabilityPool.provideToSP( - bobLUSDbal.add(toBN(dec(235534, 18))), - frontEnd_1, + bobBoldbal.add(toBN(dec(235534, 18))), { from: bob } ); await assertRevert(bobTxPromise, "revert"); }); - it("provideToSP(): reverts if user tries to provide 2^256-1 LUSD, which exceeds their balance", async () => { + it("provideToSP(): reverts if user tries to provide 2^256-1 Bold, which exceeds their balance", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice, value: dec(50, "ether") }, }); await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob, value: dec(50, "ether") }, }); @@ -447,9 +423,9 @@ contract("StabilityPool", async (accounts) => { "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ); - // Alice attempts to deposit 2^256-1 LUSD + // Alice attempts to deposit 2^256-1 Bold try { - aliceTx = await stabilityPool.provideToSP(maxBytes32, frontEnd_1, { + aliceTx = await stabilityPool.provideToSP(maxBytes32, { from: alice, }); assert.isFalse(tx.receipt.status); @@ -460,24 +436,24 @@ contract("StabilityPool", async (accounts) => { it("provideToSP(): reverts if cannot receive ETH Gain", async () => { // --- SETUP --- - // Whale deposits 1850 LUSD in StabilityPool + // Whale deposits 1850 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); - await stabilityPool.provideToSP(dec(1850, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(1850, 18), { from: whale, }); // Defaulter Troves opened await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_2 }, }); @@ -489,10 +465,9 @@ contract("StabilityPool", async (accounts) => { from: whale, }); - // NonPayable makes deposit #1: 150 LUSD - const txData1 = th.getTransactionData("provideToSP(uint256,address)", [ + // NonPayable makes deposit #1: 150 Bold + const txData1 = th.getTransactionData("provideToSP(uint256)", [ web3.utils.toHex(dec(150, 18)), - frontEnd_1, ]); const tx1 = await nonPayable.forward(stabilityPool.address, txData1); @@ -519,7 +494,7 @@ contract("StabilityPool", async (accounts) => { "NonPayable should have some accumulated gains" ); - // NonPayable tries to make deposit #2: 100LUSD (which also attempts to withdraw ETH gain) + // NonPayable tries to make deposit #2: 100Bold (which also attempts to withdraw ETH gain) const txData2 = th.getTransactionData("provideToSP(uint256,address)", [ web3.utils.toHex(dec(100, 18)), frontEnd_1, @@ -532,51 +507,51 @@ contract("StabilityPool", async (accounts) => { it("provideToSP(): doesn't impact other users' deposits or ETH gains", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: carol }, }); - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(1000, 18), { from: alice, }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_1, { from: bob }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(2000, 18), { from: bob }); + await stabilityPool.provideToSP(dec(3000, 18), { from: carol, }); // D opens a trove await openTrove({ - extraLUSDAmount: toBN(dec(300, 18)), + extraBoldAmount: toBN(dec(300, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: dennis }, }); // Would-be defaulters open troves await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_2 }, }); @@ -590,14 +565,14 @@ contract("StabilityPool", async (accounts) => { assert.isFalse(await sortedTroves.contains(defaulter_1)); assert.isFalse(await sortedTroves.contains(defaulter_2)); - const alice_LUSDDeposit_Before = ( - await stabilityPool.getCompoundedLUSDDeposit(alice) + const alice_BoldDeposit_Before = ( + await stabilityPool.getCompoundedBoldDeposit(alice) ).toString(); - const bob_LUSDDeposit_Before = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) + const bob_BoldDeposit_Before = ( + await stabilityPool.getCompoundedBoldDeposit(bob) ).toString(); - const carol_LUSDDeposit_Before = ( - await stabilityPool.getCompoundedLUSDDeposit(carol) + const carol_BoldDeposit_Before = ( + await stabilityPool.getCompoundedBoldDeposit(carol) ).toString(); const alice_ETHGain_Before = ( @@ -610,29 +585,29 @@ contract("StabilityPool", async (accounts) => { await stabilityPool.getDepositorETHGain(carol) ).toString(); - //check non-zero LUSD and ETHGain in the Stability Pool - const LUSDinSP = await stabilityPool.getTotalLUSDDeposits(); + //check non-zero Bold and ETHGain in the Stability Pool + const BoldinSP = await stabilityPool.getTotalBoldDeposits(); const ETHinSP = await stabilityPool.getETH(); - assert.isTrue(LUSDinSP.gt(mv._zeroBN)); + assert.isTrue(BoldinSP.gt(mv._zeroBN)); assert.isTrue(ETHinSP.gt(mv._zeroBN)); // D makes an SP deposit - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(1000, 18), { from: dennis, }); assert.equal( - (await stabilityPool.getCompoundedLUSDDeposit(dennis)).toString(), + (await stabilityPool.getCompoundedBoldDeposit(dennis)).toString(), dec(1000, 18) ); - const alice_LUSDDeposit_After = ( - await stabilityPool.getCompoundedLUSDDeposit(alice) + const alice_BoldDeposit_After = ( + await stabilityPool.getCompoundedBoldDeposit(alice) ).toString(); - const bob_LUSDDeposit_After = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) + const bob_BoldDeposit_After = ( + await stabilityPool.getCompoundedBoldDeposit(bob) ).toString(); - const carol_LUSDDeposit_After = ( - await stabilityPool.getCompoundedLUSDDeposit(carol) + const carol_BoldDeposit_After = ( + await stabilityPool.getCompoundedBoldDeposit(carol) ).toString(); const alice_ETHGain_After = ( @@ -646,9 +621,9 @@ contract("StabilityPool", async (accounts) => { ).toString(); // Check compounded deposits and ETH gains for A, B and C have not changed - assert.equal(alice_LUSDDeposit_Before, alice_LUSDDeposit_After); - assert.equal(bob_LUSDDeposit_Before, bob_LUSDDeposit_After); - assert.equal(carol_LUSDDeposit_Before, carol_LUSDDeposit_After); + assert.equal(alice_BoldDeposit_Before, alice_BoldDeposit_After); + assert.equal(bob_BoldDeposit_Before, bob_BoldDeposit_After); + assert.equal(carol_BoldDeposit_Before, carol_BoldDeposit_After); assert.equal(alice_ETHGain_Before, alice_ETHGain_After); assert.equal(bob_ETHGain_Before, bob_ETHGain_After); @@ -657,51 +632,51 @@ contract("StabilityPool", async (accounts) => { it("provideToSP(): doesn't impact system debt, collateral or TCR", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: carol }, }); - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(1000, 18), { from: alice, }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_1, { from: bob }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(2000, 18), { from: bob }); + await stabilityPool.provideToSP(dec(3000, 18), { from: carol, }); // D opens a trove await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: dennis }, }); // Would-be defaulters open troves await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: 0, + extraBoldAmount: 0, ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_2 }, }); @@ -715,23 +690,23 @@ contract("StabilityPool", async (accounts) => { assert.isFalse(await sortedTroves.contains(defaulter_1)); assert.isFalse(await sortedTroves.contains(defaulter_2)); - const activeDebt_Before = (await activePool.getLUSDDebt()).toString(); - const defaultedDebt_Before = (await defaultPool.getLUSDDebt()).toString(); + const activeDebt_Before = (await activePool.getBoldDebt()).toString(); + const defaultedDebt_Before = (await defaultPool.getBoldDebt()).toString(); const activeColl_Before = (await activePool.getETH()).toString(); const defaultedColl_Before = (await defaultPool.getETH()).toString(); const TCR_Before = (await th.getTCR(contracts)).toString(); // D makes an SP deposit - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(1000, 18), { from: dennis, }); assert.equal( - (await stabilityPool.getCompoundedLUSDDeposit(dennis)).toString(), + (await stabilityPool.getCompoundedBoldDeposit(dennis)).toString(), dec(1000, 18) ); - const activeDebt_After = (await activePool.getLUSDDebt()).toString(); - const defaultedDebt_After = (await defaultPool.getLUSDDebt()).toString(); + const activeDebt_After = (await activePool.getBoldDebt()).toString(); + const defaultedDebt_After = (await defaultPool.getBoldDebt()).toString(); const activeColl_After = (await activePool.getETH()).toString(); const defaultedColl_After = (await defaultPool.getETH()).toString(); const TCR_After = (await th.getTCR(contracts)).toString(); @@ -746,37 +721,37 @@ contract("StabilityPool", async (accounts) => { it("provideToSP(): doesn't impact any troves, including the caller's trove", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: carol }, }); // A and B provide to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(1000, 18), { from: alice, }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_1, { from: bob }); + await stabilityPool.provideToSP(dec(2000, 18), { from: bob }); // D opens a trove await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: dennis }, }); @@ -831,11 +806,11 @@ contract("StabilityPool", async (accounts) => { ).toString(); // D makes an SP deposit - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { + await stabilityPool.provideToSP(dec(1000, 18), { from: dennis, }); assert.equal( - (await stabilityPool.getCompoundedLUSDDeposit(dennis)).toString(), + (await stabilityPool.getCompoundedBoldDeposit(dennis)).toString(), dec(1000, 18) ); @@ -892,33 +867,33 @@ contract("StabilityPool", async (accounts) => { it("provideToSP(): doesn't protect the depositor's trove from liquidation", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: carol }, }); - // A, B provide 100 LUSD to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { + // A, B provide 100 Bold to SP + await stabilityPool.provideToSP(dec(1000, 18), { from: alice, }); - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: bob }); + await stabilityPool.provideToSP(dec(1000, 18), { from: bob }); // Confirm Bob has an active trove in the system assert.isTrue(await sortedTroves.contains(bob)); @@ -926,7 +901,7 @@ contract("StabilityPool", async (accounts) => { // Confirm Bob has a Stability deposit assert.equal( - (await stabilityPool.getCompoundedLUSDDeposit(bob)).toString(), + (await stabilityPool.getCompoundedBoldDeposit(bob)).toString(), dec(1000, 18) ); @@ -942,5085 +917,2819 @@ contract("StabilityPool", async (accounts) => { assert.equal((await troveManager.getTroveStatus(bob)).toString(), "3"); // check Bob's trove status was closed by liquidation }); - it("provideToSP(): providing 0 LUSD reverts", async () => { + it("provideToSP(): providing 0 Bold reverts", async () => { // --- SETUP --- await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: whale, value: dec(50, "ether") }, }); // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: carol }, }); - // A, B, C provides 100, 50, 30 LUSD to SP - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { + // A, B, C provides 100, 50, 30 Bold to SP + await stabilityPool.provideToSP(dec(100, 18), { from: alice, }); - await stabilityPool.provideToSP(dec(50, 18), frontEnd_1, { from: bob }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_1, { from: carol }); + await stabilityPool.provideToSP(dec(50, 18), { from: bob }); + await stabilityPool.provideToSP(dec(30, 18), { from: carol }); const bob_Deposit_Before = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) + await stabilityPool.getCompoundedBoldDeposit(bob) ).toString(); - const LUSDinSP_Before = ( - await stabilityPool.getTotalLUSDDeposits() + const BoldinSP_Before = ( + await stabilityPool.getTotalBoldDeposits() ).toString(); - assert.equal(LUSDinSP_Before, dec(180, 18)); + assert.equal(BoldinSP_Before, dec(180, 18)); - // Bob provides 0 LUSD to the Stability Pool - const txPromise_B = stabilityPool.provideToSP(0, frontEnd_1, { + // Bob provides 0 Bold to the Stability Pool + const txPromise_B = stabilityPool.provideToSP(0, { from: bob, }); await th.assertRevert(txPromise_B); }); - // --- LQTY functionality --- - it("provideToSP(), new deposit: when SP > 0, triggers LQTY reward event - increases the sum G", async () => { + it("provideToSP(), new deposit: depositor does not receive ETH gains", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: whale, value: dec(50, "ether") }, + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, }); - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); + // Whale transfers Bold to A, B + await boldToken.transfer(A, dec(100, 18), { from: whale }); + await boldToken.transfer(B, dec(200, 18), { from: whale }); + + // C, D open troves await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: C }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: D }, }); - // A provides to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); + // --- TEST --- - let currentEpoch = await stabilityPool.currentEpoch(); - let currentScale = await stabilityPool.currentScale(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); + // get current ETH balances + const A_ETHBalance_Before = await web3.eth.getBalance(A); + const B_ETHBalance_Before = await web3.eth.getBalance(B); + const C_ETHBalance_Before = await web3.eth.getBalance(C); + const D_ETHBalance_Before = await web3.eth.getBalance(D); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider + // A, B, C, D provide to SP + const A_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(100, 18), { + from: A, + gasPrice: GAS_PRICE, + }) + ); + const B_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(200, 18), { + from: B, + gasPrice: GAS_PRICE, + }) + ); + const C_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(300, 18), { + from: C, + gasPrice: GAS_PRICE, + }) + ); + const D_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(400, 18), { + from: D, + gasPrice: GAS_PRICE, + }) ); - // B provides to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: B }); + // ETH balances before minus gas used + const A_expectedBalance = A_ETHBalance_Before - A_GAS_Used; + const B_expectedBalance = B_ETHBalance_Before - B_GAS_Used; + const C_expectedBalance = C_ETHBalance_Before - C_GAS_Used; + const D_expectedBalance = D_ETHBalance_Before - D_GAS_Used; - currentEpoch = await stabilityPool.currentEpoch(); - currentScale = await stabilityPool.currentScale(); - const G_After = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); + // Get ETH balances after + const A_ETHBalance_After = await web3.eth.getBalance(A); + const B_ETHBalance_After = await web3.eth.getBalance(B); + const C_ETHBalance_After = await web3.eth.getBalance(C); + const D_ETHBalance_After = await web3.eth.getBalance(D); - // Expect G has increased from the LQTY reward event triggered - assert.isTrue(G_After.gt(G_Before)); + // Check ETH balances have not changed + assert.equal(A_ETHBalance_After, A_expectedBalance); + assert.equal(B_ETHBalance_After, B_expectedBalance); + assert.equal(C_ETHBalance_After, C_expectedBalance); + assert.equal(D_ETHBalance_After, D_expectedBalance); }); - it("provideToSP(), new deposit: when SP is empty, doesn't update G", async () => { + it("provideToSP(), new deposit after past full withdrawal: depositor does not receive ETH gains", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: whale, value: dec(50, "ether") }, + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, }); - // A, B, C open troves and make Stability Pool deposits + // Whale transfers Bold to A, B + await boldToken.transfer(A, dec(1000, 18), { from: whale }); + await boldToken.transfer(B, dec(1000, 18), { from: whale }); + + // C, D open troves await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(4000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: C }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(5000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: D }, }); + await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_1 }, }); - // A provides to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); + // --- SETUP --- + // A, B, C, D provide to SP + await stabilityPool.provideToSP(dec(105, 18), { from: A }); + await stabilityPool.provideToSP(dec(105, 18), { from: B }); + await stabilityPool.provideToSP(dec(105, 18), { from: C }); + await stabilityPool.provideToSP(dec(105, 18), { from: D }); + // time passes await th.fastForwardTime( timeValues.SECONDS_IN_ONE_HOUR, web3.currentProvider ); - // A withdraws - await stabilityPool.withdrawFromSP(dec(1000, 18), { from: A }); + // B deposits + await stabilityPool.provideToSP(dec(5, 18), { from: B }); - // Check SP is empty - assert.equal(await stabilityPool.getTotalLUSDDeposits(), "0"); + // Price drops, defaulter is liquidated, A, B, C, D earn ETH + await priceFeed.setPrice(dec(105, 18)); + assert.isFalse(await th.checkRecoveryMode(contracts)); - // Check G is non-zero - let currentEpoch = await stabilityPool.currentEpoch(); - let currentScale = await stabilityPool.currentScale(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); + await troveManager.liquidate(defaulter_1); - assert.isTrue(G_Before.gt(toBN("0"))); + // Price bounces back + await priceFeed.setPrice(dec(200, 18)); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // A B,C, D fully withdraw from the pool + await stabilityPool.withdrawFromSP(dec(105, 18), { from: A }); + await stabilityPool.withdrawFromSP(dec(105, 18), { from: B }); + await stabilityPool.withdrawFromSP(dec(105, 18), { from: C }); + await stabilityPool.withdrawFromSP(dec(105, 18), { from: D }); - // B provides to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: B }); + // --- TEST --- - currentEpoch = await stabilityPool.currentEpoch(); - currentScale = await stabilityPool.currentScale(); - const G_After = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale + // get current ETH balances + const A_ETHBalance_Before = await web3.eth.getBalance(A); + const B_ETHBalance_Before = await web3.eth.getBalance(B); + const C_ETHBalance_Before = await web3.eth.getBalance(C); + const D_ETHBalance_Before = await web3.eth.getBalance(D); + + // A, B, C, D provide to SP + const A_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(100, 18), { + from: A, + gasPrice: GAS_PRICE, + gasPrice: GAS_PRICE, + }) + ); + const B_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(200, 18), { + from: B, + gasPrice: GAS_PRICE, + gasPrice: GAS_PRICE, + }) + ); + const C_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(300, 18), { + from: C, + gasPrice: GAS_PRICE, + gasPrice: GAS_PRICE, + }) ); + const D_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(400, 18), { + from: D, + gasPrice: GAS_PRICE, + gasPrice: GAS_PRICE, + }) + ); + + // ETH balances before minus gas used + const A_expectedBalance = A_ETHBalance_Before - A_GAS_Used; + const B_expectedBalance = B_ETHBalance_Before - B_GAS_Used; + const C_expectedBalance = C_ETHBalance_Before - C_GAS_Used; + const D_expectedBalance = D_ETHBalance_Before - D_GAS_Used; + + // Get ETH balances after + const A_ETHBalance_After = await web3.eth.getBalance(A); + const B_ETHBalance_After = await web3.eth.getBalance(B); + const C_ETHBalance_After = await web3.eth.getBalance(C); + const D_ETHBalance_After = await web3.eth.getBalance(D); - // Expect G has not changed - assert.isTrue(G_After.eq(G_Before)); + // Check ETH balances have not changed + assert.equal(A_ETHBalance_After, A_expectedBalance); + assert.equal(B_ETHBalance_After, B_expectedBalance); + assert.equal(C_ETHBalance_After, C_expectedBalance); + assert.equal(D_ETHBalance_After, D_expectedBalance); }); - it("provideToSP(), new deposit: sets the correct front end tag", async () => { + it("provideToSP(): reverts when amount is zero", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: whale, value: dec(50, "ether") }, + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, }); - // A, B, C, D open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: A }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: B }, }); - await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, - }); - // Check A, B, C D have no front end tags - const A_tagBefore = await getFrontEndTag(stabilityPool, A); - const B_tagBefore = await getFrontEndTag(stabilityPool, B); - const C_tagBefore = await getFrontEndTag(stabilityPool, C); - const D_tagBefore = await getFrontEndTag(stabilityPool, D); - - assert.equal(A_tagBefore, ZERO_ADDRESS); - assert.equal(B_tagBefore, ZERO_ADDRESS); - assert.equal(C_tagBefore, ZERO_ADDRESS); - assert.equal(D_tagBefore, ZERO_ADDRESS); - - // A, B, C, D provides to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_3, { from: C }); - await stabilityPool.provideToSP(dec(4000, 18), ZERO_ADDRESS, { from: D }); // transacts directly, no front end - - // Check A, B, C D have no front end tags - const A_tagAfter = await getFrontEndTag(stabilityPool, A); - const B_tagAfter = await getFrontEndTag(stabilityPool, B); - const C_tagAfter = await getFrontEndTag(stabilityPool, C); - const D_tagAfter = await getFrontEndTag(stabilityPool, D); - - // Check front end tags are correctly set - assert.equal(A_tagAfter, frontEnd_1); - assert.equal(B_tagAfter, frontEnd_2); - assert.equal(C_tagAfter, frontEnd_3); - assert.equal(D_tagAfter, ZERO_ADDRESS); + // Whale transfers Bold to C, D + await boldToken.transfer(C, dec(100, 18), { from: whale }); + await boldToken.transfer(D, dec(100, 18), { from: whale }); + + txPromise_A = stabilityPool.provideToSP(0, { from: A }); + txPromise_B = stabilityPool.provideToSP(0, { from: B }); + txPromise_C = stabilityPool.provideToSP(0, { from: C }); + txPromise_D = stabilityPool.provideToSP(0, { from: D }); + + await th.assertRevert( + txPromise_A, + "StabilityPool: Amount must be non-zero" + ); + await th.assertRevert( + txPromise_B, + "StabilityPool: Amount must be non-zero" + ); + await th.assertRevert( + txPromise_C, + "StabilityPool: Amount must be non-zero" + ); + await th.assertRevert( + txPromise_D, + "StabilityPool: Amount must be non-zero" + ); }); - it("provideToSP(), new deposit: depositor does not receive any LQTY rewards", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: whale, value: dec(50, "ether") }, - }); + // --- withdrawFromSP --- - // A, B, open troves + it("withdrawFromSP(): reverts when user has no active deposit", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), + extraBoldAmount: toBN(dec(100, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), + extraBoldAmount: toBN(dec(100, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: bob }, }); - // Get A, B, C LQTY balances before and confirm they're zero - const A_LQTYBalance_Before = await lqtyToken.balanceOf(A); - const B_LQTYBalance_Before = await lqtyToken.balanceOf(B); + await stabilityPool.provideToSP(dec(100, 18), { + from: alice, + }); - assert.equal(A_LQTYBalance_Before, "0"); - assert.equal(B_LQTYBalance_Before, "0"); + const alice_initialDeposit = ( + await stabilityPool.deposits(alice) + ).toString(); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + const bob_initialDeposit = ( + await stabilityPool.deposits(bob) + ).toString(); - // A, B provide to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(2000, 18), ZERO_ADDRESS, { from: B }); + assert.equal(alice_initialDeposit, dec(100, 18)); + assert.equal(bob_initialDeposit, "0"); - // Get A, B, C LQTY balances after, and confirm they're still zero - const A_LQTYBalance_After = await lqtyToken.balanceOf(A); - const B_LQTYBalance_After = await lqtyToken.balanceOf(B); + const txAlice = await stabilityPool.withdrawFromSP(dec(100, 18), { + from: alice, + }); + assert.isTrue(txAlice.receipt.status); - assert.equal(A_LQTYBalance_After, "0"); - assert.equal(B_LQTYBalance_After, "0"); + try { + const txBob = await stabilityPool.withdrawFromSP(dec(100, 18), { + from: bob, + }); + assert.isFalse(txBob.receipt.status); + } catch (err) { + assert.include(err.message, "revert"); + // TODO: infamous issue #99 + //assert.include(err.message, "User must have a non-zero deposit") + } }); - it("provideToSP(), new deposit after past full withdrawal: depositor does not receive any LQTY rewards", async () => { + it("withdrawFromSP(): reverts when amount > 0 and system has an undercollateralized trove", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, + extraBoldAmount: toBN(dec(100, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: alice }, }); - // A, B, C, open troves - await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + await stabilityPool.provideToSP(dec(100, 18), { + from: alice, }); + + const alice_initialDeposit = ( + await stabilityPool.deposits(alice) + ).toString(); + assert.equal(alice_initialDeposit, dec(100, 18)); + + // defaulter opens trove await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: defaulter_1 }, }); - await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(4000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, - }); - - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // --- SETUP --- - - const initialDeposit_A = await boldToken.balanceOf(A); - const initialDeposit_B = await boldToken.balanceOf(B); - // A, B provide to SP - await stabilityPool.provideToSP(initialDeposit_A, frontEnd_1, { - from: A, - }); - await stabilityPool.provideToSP(initialDeposit_B, frontEnd_2, { - from: B, - }); - - // time passes - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // C deposits. A, and B earn LQTY - await stabilityPool.provideToSP(dec(5, 18), ZERO_ADDRESS, { from: C }); - - // Price drops, defaulter is liquidated, A, B and C earn ETH - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - - await troveManager.liquidate(defaulter_1); - - // price bounces back to 200 - await priceFeed.setPrice(dec(200, 18)); - - // A and B fully withdraw from the pool - await stabilityPool.withdrawFromSP(initialDeposit_A, { from: A }); - await stabilityPool.withdrawFromSP(initialDeposit_B, { from: B }); - - // --- TEST --- - // Get A, B, C LQTY balances before and confirm they're non-zero - const A_LQTYBalance_Before = await lqtyToken.balanceOf(A); - const B_LQTYBalance_Before = await lqtyToken.balanceOf(B); - assert.isTrue(A_LQTYBalance_Before.gt(toBN("0"))); - assert.isTrue(B_LQTYBalance_Before.gt(toBN("0"))); + // ETH drops, defaulter is in liquidation range (but not liquidated yet) + await priceFeed.setPrice(dec(100, 18)); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider + await th.assertRevert( + stabilityPool.withdrawFromSP(dec(100, 18), { from: alice }) ); - - // A, B provide to SP - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(200, 18), ZERO_ADDRESS, { from: B }); - - // Get A, B, C LQTY balances after, and confirm they have not changed - const A_LQTYBalance_After = await lqtyToken.balanceOf(A); - const B_LQTYBalance_After = await lqtyToken.balanceOf(B); - - assert.isTrue(A_LQTYBalance_After.eq(A_LQTYBalance_Before)); - assert.isTrue(B_LQTYBalance_After.eq(B_LQTYBalance_Before)); }); - it("provideToSP(), new eligible deposit: tagged front end receives LQTY rewards", async () => { + it("withdrawFromSP(): partial retrieval - retrieves correct Bold amount and the entire ETH Gain, and updates deposit", async () => { + // --- SETUP --- + // Whale deposits 185000 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1, 24)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - - // A, B, C, open troves - await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, }); + + // 2 Troves opened await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + extraParams: { from: defaulter_2 }, }); + + // --- TEST --- + + // Alice makes deposit #1: 15000 Bold await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: E }, + extraBoldAmount: toBN(dec(15000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: alice }, }); - await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: F }, + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - // D, E, F provide to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: D }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_2, { from: E }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_3, { from: F }); + // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active + await priceFeed.setPrice(dec(105, 18)); - // Get F1, F2, F3 LQTY balances before, and confirm they're zero - const frontEnd_1_LQTYBalance_Before = await lqtyToken.balanceOf( - frontEnd_1 - ); - const frontEnd_2_LQTYBalance_Before = await lqtyToken.balanceOf( - frontEnd_2 + // 2 users with Trove with 170 Bold drawn are closed + const liquidationTX_1 = await troveManager.liquidate(defaulter_1, { + from: owner, + }); // 170 Bold closed + const liquidationTX_2 = await troveManager.liquidate(defaulter_2, { + from: owner, + }); // 170 Bold closed + + const [liquidatedDebt_1] = await th.getEmittedLiquidationValues( + liquidationTX_1 ); - const frontEnd_3_LQTYBalance_Before = await lqtyToken.balanceOf( - frontEnd_3 + const [liquidatedDebt_2] = await th.getEmittedLiquidationValues( + liquidationTX_2 ); - assert.equal(frontEnd_1_LQTYBalance_Before, "0"); - assert.equal(frontEnd_2_LQTYBalance_Before, "0"); - assert.equal(frontEnd_3_LQTYBalance_Before, "0"); + // Alice BoldLoss is ((15000/200000) * liquidatedDebt), for each liquidation + const expectedBoldLoss_A = liquidatedDebt_1 + .mul(toBN(dec(15000, 18))) + .div(toBN(dec(200000, 18))) + .add( + liquidatedDebt_2.mul(toBN(dec(15000, 18))).div(toBN(dec(200000, 18))) + ); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider + const expectedCompoundedBoldDeposit_A = toBN(dec(15000, 18)).sub( + expectedBoldLoss_A ); + const compoundedBoldDeposit_A = + await stabilityPool.getCompoundedBoldDeposit(alice); - // console.log(`LQTYSupplyCap before: ${await communityIssuance.LQTYSupplyCap()}`) - // console.log(`totalLQTYIssued before: ${await communityIssuance.totalLQTYIssued()}`) - // console.log(`LQTY balance of CI before: ${await lqtyToken.balanceOf(communityIssuance.address)}`) - - // A, B, C provide to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_3, { from: C }); + assert.isAtMost( + th.getDifference( + expectedCompoundedBoldDeposit_A, + compoundedBoldDeposit_A + ), + 100000 + ); - // console.log(`LQTYSupplyCap after: ${await communityIssuance.LQTYSupplyCap()}`) - // console.log(`totalLQTYIssued after: ${await communityIssuance.totalLQTYIssued()}`) - // console.log(`LQTY balance of CI after: ${await lqtyToken.balanceOf(communityIssuance.address)}`) + // Alice retrieves part of her entitled Bold: 9000 Bold + await stabilityPool.withdrawFromSP(dec(9000, 18), { from: alice }); - // Get F1, F2, F3 LQTY balances after, and confirm they have increased - const frontEnd_1_LQTYBalance_After = await lqtyToken.balanceOf( - frontEnd_1 - ); - const frontEnd_2_LQTYBalance_After = await lqtyToken.balanceOf( - frontEnd_2 - ); - const frontEnd_3_LQTYBalance_After = await lqtyToken.balanceOf( - frontEnd_3 + const expectedNewDeposit_A = compoundedBoldDeposit_A.sub( + toBN(dec(9000, 18)) ); - assert.isTrue( - frontEnd_1_LQTYBalance_After.gt(frontEnd_1_LQTYBalance_Before) - ); - assert.isTrue( - frontEnd_2_LQTYBalance_After.gt(frontEnd_2_LQTYBalance_Before) + // check Alice's deposit has been updated to equal her compounded deposit minus her withdrawal */ + const newDeposit = (await stabilityPool.deposits(alice)).toString(); + assert.isAtMost( + th.getDifference(newDeposit, expectedNewDeposit_A), + 100000 ); - assert.isTrue( - frontEnd_3_LQTYBalance_After.gt(frontEnd_3_LQTYBalance_Before) + + // Expect Alice has withdrawn all ETH gain + const alice_pendingETHGain = await stabilityPool.getDepositorETHGain( + alice ); + assert.equal(alice_pendingETHGain, 0); }); - it("provideToSP(), new eligible deposit: tagged front end's stake increases", async () => { + it("withdrawFromSP(): partial retrieval - leaves the correct amount of Bold in the Stability Pool", async () => { + // --- SETUP --- + // Whale deposits 185000 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1, 24)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, + }); - // A, B, C, open troves + // 2 Troves opened await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: defaulter_2 }, }); + // --- TEST --- + + // Alice makes deposit #1: 15000 Bold await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraBoldAmount: toBN(dec(15000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: alice }, + }); + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - // Get front ends' stakes before - const F1_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_3); + const SP_Bold_Before = await stabilityPool.getTotalBoldDeposits(); + assert.equal(SP_Bold_Before, dec(200000, 18)); + + // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active + await priceFeed.setPrice(dec(105, 18)); + + // 2 users liquidated + const liquidationTX_1 = await troveManager.liquidate(defaulter_1, { + from: owner, + }); + const liquidationTX_2 = await troveManager.liquidate(defaulter_2, { + from: owner, + }); - const deposit_A = dec(1000, 18); - const deposit_B = dec(2000, 18); - const deposit_C = dec(3000, 18); + const [liquidatedDebt_1] = await th.getEmittedLiquidationValues( + liquidationTX_1 + ); + const [liquidatedDebt_2] = await th.getEmittedLiquidationValues( + liquidationTX_2 + ); - // A, B, C provide to SP - await stabilityPool.provideToSP(deposit_A, frontEnd_1, { from: A }); - await stabilityPool.provideToSP(deposit_B, frontEnd_2, { from: B }); - await stabilityPool.provideToSP(deposit_C, frontEnd_3, { from: C }); + // Alice retrieves part of her entitled Bold: 9000 Bold + await stabilityPool.withdrawFromSP(dec(9000, 18), { from: alice }); - // Get front ends' stakes after - const F1_Stake_After = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_After = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_After = await stabilityPool.frontEndStakes(frontEnd_3); + /* Check SP has reduced from 2 liquidations and Alice's withdrawal + Expect Bold in SP = (200000 - liquidatedDebt_1 - liquidatedDebt_2 - 9000) */ + const expectedSPBold = toBN(dec(200000, 18)) + .sub(toBN(liquidatedDebt_1)) + .sub(toBN(liquidatedDebt_2)) + .sub(toBN(dec(9000, 18))); - const F1_Diff = F1_Stake_After.sub(F1_Stake_Before); - const F2_Diff = F2_Stake_After.sub(F2_Stake_Before); - const F3_Diff = F3_Stake_After.sub(F3_Stake_Before); + const SP_Bold_After = ( + await stabilityPool.getTotalBoldDeposits() + ).toString(); - // Check front ends' stakes have increased by amount equal to the deposit made through them - assert.equal(F1_Diff, deposit_A); - assert.equal(F2_Diff, deposit_B); - assert.equal(F3_Diff, deposit_C); + th.assertIsApproximatelyEqual(SP_Bold_After, expectedSPBold); }); - it("provideToSP(), new eligible deposit: tagged front end's snapshots update", async () => { + it("withdrawFromSP(): full retrieval - leaves the correct amount of Bold in the Stability Pool", async () => { + // --- SETUP --- + // Whale deposits 185000 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - - // A, B, C, open troves - await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, }); + + // 2 Troves opened await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_2 }, }); - // D opens trove - await openTrove({ - extraLUSDAmount: toBN(dec(4000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, - }); + // --- TEST --- + // Alice makes deposit #1 await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + extraBoldAmount: toBN(dec(15000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: alice }, + }); + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - // --- SETUP --- - - await stabilityPool.provideToSP(dec(2000, 18), ZERO_ADDRESS, { from: D }); - - // fastforward time then make an SP deposit, to make G > 0 - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - await stabilityPool.provideToSP(dec(2000, 18), ZERO_ADDRESS, { from: D }); + const SP_Bold_Before = await stabilityPool.getTotalBoldDeposits(); + assert.equal(SP_Bold_Before, dec(200000, 18)); - // Perform a liquidation to make 0 < P < 1, and S > 0 + // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - - await troveManager.liquidate(defaulter_1); - const currentEpoch = await stabilityPool.currentEpoch(); - const currentScale = await stabilityPool.currentScale(); + // 2 defaulters liquidated + const liquidationTX_1 = await troveManager.liquidate(defaulter_1, { + from: owner, + }); + const liquidationTX_2 = await troveManager.liquidate(defaulter_2, { + from: owner, + }); - const S_Before = await stabilityPool.epochToScaleToSum( - currentEpoch, - currentScale + const [liquidatedDebt_1] = await th.getEmittedLiquidationValues( + liquidationTX_1 ); - const P_Before = await stabilityPool.P(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale + const [liquidatedDebt_2] = await th.getEmittedLiquidationValues( + liquidationTX_2 ); - // Confirm 0 < P < 1 - assert.isTrue(P_Before.gt(toBN("0")) && P_Before.lt(toBN(dec(1, 18)))); - // Confirm S, G are both > 0 - assert.isTrue(S_Before.gt(toBN("0"))); - assert.isTrue(G_Before.gt(toBN("0"))); - - // Get front ends' snapshots before - for (frontEnd of [frontEnd_1, frontEnd_2, frontEnd_3]) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); - - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends, since S corresponds to ETH gain) - assert.equal(snapshot[1], "0"); // P - assert.equal(snapshot[2], "0"); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - - const deposit_A = dec(1000, 18); - const deposit_B = dec(2000, 18); - const deposit_C = dec(3000, 18); - - // --- TEST --- - - // A, B, C provide to SP - const G1 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.provideToSP(deposit_A, frontEnd_1, { from: A }); + // Alice BoldLoss is ((15000/200000) * liquidatedDebt), for each liquidation + const expectedBoldLoss_A = liquidatedDebt_1 + .mul(toBN(dec(15000, 18))) + .div(toBN(dec(200000, 18))) + .add( + liquidatedDebt_2.mul(toBN(dec(15000, 18))).div(toBN(dec(200000, 18))) + ); - const G2 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch + const expectedCompoundedBoldDeposit_A = toBN(dec(15000, 18)).sub( + expectedBoldLoss_A ); - await stabilityPool.provideToSP(deposit_B, frontEnd_2, { from: B }); + const compoundedBoldDeposit_A = + await stabilityPool.getCompoundedBoldDeposit(alice); - const G3 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch + assert.isAtMost( + th.getDifference( + expectedCompoundedBoldDeposit_A, + compoundedBoldDeposit_A + ), + 100000 ); - await stabilityPool.provideToSP(deposit_C, frontEnd_3, { from: C }); - const frontEnds = [frontEnd_1, frontEnd_2, frontEnd_3]; - const G_Values = [G1, G2, G3]; + const BoldinSPBefore = await stabilityPool.getTotalBoldDeposits(); - // Map frontEnds to the value of G at time the deposit was made - frontEndToG = th.zipToObject(frontEnds, G_Values); + // Alice retrieves all of her entitled Bold: + await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); - // Get front ends' snapshots after - for (const [frontEnd, G] of Object.entries(frontEndToG)) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); + const expectedBoldinSPAfter = BoldinSPBefore.sub(compoundedBoldDeposit_A); - // Check snapshots are the expected values - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends) - assert.isTrue(snapshot[1].eq(P_Before)); // P - assert.isTrue(snapshot[2].eq(G)); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } + const BoldinSPAfter = await stabilityPool.getTotalBoldDeposits(); + assert.isAtMost( + th.getDifference(expectedBoldinSPAfter, BoldinSPAfter), + 100000 + ); }); - it("provideToSP(), new deposit: depositor does not receive ETH gains", async () => { + it("withdrawFromSP(): Subsequent deposit and withdrawal attempt from same account, with no intermediate liquidations, withdraws zero ETH", async () => { + // --- SETUP --- + // Whale deposits 1850 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); + await stabilityPool.provideToSP(dec(18500, 18), { + from: whale, + }); - // Whale transfers LUSD to A, B - await boldToken.transfer(A, dec(100, 18), { from: whale }); - await boldToken.transfer(B, dec(200, 18), { from: whale }); - - // C, D open troves + // 2 defaulters open await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + extraParams: { from: defaulter_2 }, }); // --- TEST --- - // get current ETH balances - const A_ETHBalance_Before = await web3.eth.getBalance(A); - const B_ETHBalance_Before = await web3.eth.getBalance(B); - const C_ETHBalance_Before = await web3.eth.getBalance(C); - const D_ETHBalance_Before = await web3.eth.getBalance(D); - - // A, B, C, D provide to SP - const A_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { - from: A, - gasPrice: GAS_PRICE, - }) - ); - const B_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(200, 18), ZERO_ADDRESS, { - from: B, - gasPrice: GAS_PRICE, - }) - ); - const C_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(300, 18), frontEnd_2, { - from: C, - gasPrice: GAS_PRICE, - }) - ); - const D_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(400, 18), ZERO_ADDRESS, { - from: D, - gasPrice: GAS_PRICE, - }) - ); - - // ETH balances before minus gas used - const A_expectedBalance = A_ETHBalance_Before - A_GAS_Used; - const B_expectedBalance = B_ETHBalance_Before - B_GAS_Used; - const C_expectedBalance = C_ETHBalance_Before - C_GAS_Used; - const D_expectedBalance = D_ETHBalance_Before - D_GAS_Used; - - // Get ETH balances after - const A_ETHBalance_After = await web3.eth.getBalance(A); - const B_ETHBalance_After = await web3.eth.getBalance(B); - const C_ETHBalance_After = await web3.eth.getBalance(C); - const D_ETHBalance_After = await web3.eth.getBalance(D); - - // Check ETH balances have not changed - assert.equal(A_ETHBalance_After, A_expectedBalance); - assert.equal(B_ETHBalance_After, B_expectedBalance); - assert.equal(C_ETHBalance_After, C_expectedBalance); - assert.equal(D_ETHBalance_After, D_expectedBalance); - }); - - it("provideToSP(), new deposit after past full withdrawal: depositor does not receive ETH gains", async () => { + // Alice makes deposit #1: 15000 Bold await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(15000, 18)), ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // Whale transfers LUSD to A, B - await boldToken.transfer(A, dec(1000, 18), { from: whale }); - await boldToken.transfer(B, dec(1000, 18), { from: whale }); - - // C, D open troves - await openTrove({ - extraLUSDAmount: toBN(dec(4000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(5000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + extraParams: { from: alice }, }); - - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - // --- SETUP --- - // A, B, C, D provide to SP - await stabilityPool.provideToSP(dec(105, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(105, 18), ZERO_ADDRESS, { from: B }); - await stabilityPool.provideToSP(dec(105, 18), frontEnd_1, { from: C }); - await stabilityPool.provideToSP(dec(105, 18), ZERO_ADDRESS, { from: D }); - - // time passes - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // B deposits. A,B,C,D earn LQTY - await stabilityPool.provideToSP(dec(5, 18), ZERO_ADDRESS, { from: B }); - - // Price drops, defaulter is liquidated, A, B, C, D earn ETH + // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - await troveManager.liquidate(defaulter_1); - - // Price bounces back - await priceFeed.setPrice(dec(200, 18)); + // defaulters liquidated + await troveManager.liquidate(defaulter_1, { from: owner }); + await troveManager.liquidate(defaulter_2, { from: owner }); - // A B,C, D fully withdraw from the pool - await stabilityPool.withdrawFromSP(dec(105, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(105, 18), { from: B }); - await stabilityPool.withdrawFromSP(dec(105, 18), { from: C }); - await stabilityPool.withdrawFromSP(dec(105, 18), { from: D }); + // Alice retrieves all of her entitled Bold: + await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); + assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - // --- TEST --- + // Alice makes second deposit + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, + }); + assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - // get current ETH balances - const A_ETHBalance_Before = await web3.eth.getBalance(A); - const B_ETHBalance_Before = await web3.eth.getBalance(B); - const C_ETHBalance_Before = await web3.eth.getBalance(C); - const D_ETHBalance_Before = await web3.eth.getBalance(D); + const ETHinSP_Before = (await stabilityPool.getETH()).toString(); - // A, B, C, D provide to SP - const A_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { - from: A, - gasPrice: GAS_PRICE, - gasPrice: GAS_PRICE, - }) - ); - const B_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(200, 18), ZERO_ADDRESS, { - from: B, - gasPrice: GAS_PRICE, - gasPrice: GAS_PRICE, - }) - ); - const C_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(300, 18), frontEnd_2, { - from: C, - gasPrice: GAS_PRICE, - gasPrice: GAS_PRICE, - }) - ); - const D_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(400, 18), ZERO_ADDRESS, { - from: D, - gasPrice: GAS_PRICE, - gasPrice: GAS_PRICE, - }) - ); + // Alice attempts second withdrawal + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: alice }); + assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - // ETH balances before minus gas used - const A_expectedBalance = A_ETHBalance_Before - A_GAS_Used; - const B_expectedBalance = B_ETHBalance_Before - B_GAS_Used; - const C_expectedBalance = C_ETHBalance_Before - C_GAS_Used; - const D_expectedBalance = D_ETHBalance_Before - D_GAS_Used; + // Check ETH in pool does not change + const ETHinSP_1 = (await stabilityPool.getETH()).toString(); + assert.equal(ETHinSP_Before, ETHinSP_1); - // Get ETH balances after - const A_ETHBalance_After = await web3.eth.getBalance(A); - const B_ETHBalance_After = await web3.eth.getBalance(B); - const C_ETHBalance_After = await web3.eth.getBalance(C); - const D_ETHBalance_After = await web3.eth.getBalance(D); + // Third deposit + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, + }); + assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - // Check ETH balances have not changed - assert.equal(A_ETHBalance_After, A_expectedBalance); - assert.equal(B_ETHBalance_After, B_expectedBalance); - assert.equal(C_ETHBalance_After, C_expectedBalance); - assert.equal(D_ETHBalance_After, D_expectedBalance); + // Alice attempts third withdrawal (this time, frm SP to Trove) + const txPromise_A = stabilityPool.withdrawETHGainToTrove(alice, alice, { + from: alice, + }); + await th.assertRevert(txPromise_A); }); - it("provideToSP(), topup: triggers LQTY reward event - increases the sum G", async () => { + it("withdrawFromSP(): it correctly updates the user's Bold and ETH snapshots of entitled reward per unit staked", async () => { + // --- SETUP --- + // Whale deposits 185000 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, }); + + // 2 defaulters open await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_2 }, }); - // A, B, C provide to SP - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(50, 18), frontEnd_1, { from: B }); - await stabilityPool.provideToSP(dec(50, 18), frontEnd_1, { from: C }); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - const G_Before = await stabilityPool.epochToScaleToG(0, 0); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // B tops up - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { from: B }); - - const G_After = await stabilityPool.epochToScaleToG(0, 0); - - // Expect G has increased from the LQTY reward event triggered by B's topup - assert.isTrue(G_After.gt(G_Before)); - }); + // --- TEST --- - it("provideToSP(), topup from different front end: doesn't change the front end tag", async () => { + // Alice makes deposit #1: 15000 Bold await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(15000, 18)), ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, + extraParams: { from: alice }, + }); + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - // whale transfer to troves D and E - await boldToken.transfer(D, dec(100, 18), { from: whale }); - await boldToken.transfer(E, dec(200, 18), { from: whale }); + // check 'Before' snapshots + const alice_snapshot_Before = await stabilityPool.depositSnapshots(alice); + const alice_snapshot_S_Before = alice_snapshot_Before[0].toString(); + const alice_snapshot_P_Before = alice_snapshot_Before[1].toString(); + assert.equal(alice_snapshot_S_Before, 0); + assert.equal(alice_snapshot_P_Before, "1000000000000000000"); - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(200, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(300, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); + // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active + await priceFeed.setPrice(dec(105, 18)); - // A, B, C, D, E provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), ZERO_ADDRESS, { from: C }); - await stabilityPool.provideToSP(dec(40, 18), frontEnd_1, { from: D }); - await stabilityPool.provideToSP(dec(50, 18), ZERO_ADDRESS, { from: E }); + // 2 defaulters liquidated + await troveManager.liquidate(defaulter_1, { from: owner }); + await troveManager.liquidate(defaulter_2, { from: owner }); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // Alice retrieves part of her entitled Bold: 9000 Bold + await stabilityPool.withdrawFromSP(dec(9000, 18), { from: alice }); - // A, B, C, D, E top up, from different front ends - await stabilityPool.provideToSP(dec(10, 18), frontEnd_2, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_1, { from: B }); - await stabilityPool.provideToSP(dec(15, 18), frontEnd_3, { from: C }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: D }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: E }); - - const frontEndTag_A = (await stabilityPool.deposits(A))[1]; - const frontEndTag_B = (await stabilityPool.deposits(B))[1]; - const frontEndTag_C = (await stabilityPool.deposits(C))[1]; - const frontEndTag_D = (await stabilityPool.deposits(D))[1]; - const frontEndTag_E = (await stabilityPool.deposits(E))[1]; - - // Check deposits are still tagged with their original front end - assert.equal(frontEndTag_A, frontEnd_1); - assert.equal(frontEndTag_B, frontEnd_2); - assert.equal(frontEndTag_C, ZERO_ADDRESS); - assert.equal(frontEndTag_D, frontEnd_1); - assert.equal(frontEndTag_E, ZERO_ADDRESS); + const P = (await stabilityPool.P()).toString(); + const S = (await stabilityPool.epochToScaleToSum(0, 0)).toString(); + // check 'After' snapshots + const alice_snapshot_After = await stabilityPool.depositSnapshots(alice); + const alice_snapshot_S_After = alice_snapshot_After[0].toString(); + const alice_snapshot_P_After = alice_snapshot_After[1].toString(); + assert.equal(alice_snapshot_S_After, S); + assert.equal(alice_snapshot_P_After, P); }); - it("provideToSP(), topup: depositor receives LQTY rewards", async () => { + it("withdrawFromSP(): decreases StabilityPool ETH", async () => { + // --- SETUP --- + // Whale deposits 185000 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, + }); - // A, B, C open troves + // 1 defaulter opens await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: defaulter_1 }, }); + + // --- TEST --- + + // Alice makes deposit #1: 15000 Bold await openTrove({ - extraLUSDAmount: toBN(dec(200, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraBoldAmount: toBN(dec(15000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: alice }, }); - await openTrove({ - extraLUSDAmount: toBN(dec(300, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - // A, B, C, provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), ZERO_ADDRESS, { from: C }); + // price drops: defaulter's Trove falls below MCR, alice and whale Trove remain active + await priceFeed.setPrice("100000000000000000000"); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // defaulter's Trove is closed. + const liquidationTx_1 = await troveManager.liquidate(defaulter_1, { + from: owner, + }); // 180 Bold closed + const [, liquidatedColl] = + th.getEmittedLiquidationValues(liquidationTx_1); + + //Get ActivePool and StabilityPool Ether before retrieval: + const active_ETH_Before = await activePool.getETH(); + const stability_ETH_Before = await stabilityPool.getETH(); + + // Expect alice to be entitled to 15000/200000 of the liquidated coll + const aliceExpectedETHGain = liquidatedColl + .mul(toBN(dec(15000, 18))) + .div(toBN(dec(200000, 18))); + const aliceETHGain = await stabilityPool.getDepositorETHGain(alice); + assert.isTrue(aliceExpectedETHGain.eq(aliceETHGain)); - // Get A, B, C LQTY balance before - const A_LQTYBalance_Before = await lqtyToken.balanceOf(A); - const B_LQTYBalance_Before = await lqtyToken.balanceOf(B); - const C_LQTYBalance_Before = await lqtyToken.balanceOf(C); - - // A, B, C top up - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), ZERO_ADDRESS, { from: C }); - - // Get LQTY balance after - const A_LQTYBalance_After = await lqtyToken.balanceOf(A); - const B_LQTYBalance_After = await lqtyToken.balanceOf(B); - const C_LQTYBalance_After = await lqtyToken.balanceOf(C); - - // Check LQTY Balance of A, B, C has increased - assert.isTrue(A_LQTYBalance_After.gt(A_LQTYBalance_Before)); - assert.isTrue(B_LQTYBalance_After.gt(B_LQTYBalance_Before)); - assert.isTrue(C_LQTYBalance_After.gt(C_LQTYBalance_Before)); + // Alice retrieves all of her deposit + await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); + + const active_ETH_After = await activePool.getETH(); + const stability_ETH_After = await stabilityPool.getETH(); + + const active_ETH_Difference = active_ETH_Before.sub(active_ETH_After); + const stability_ETH_Difference = + stability_ETH_Before.sub(stability_ETH_After); + + assert.equal(active_ETH_Difference, "0"); + + // Expect StabilityPool to have decreased by Alice's ETHGain + assert.isAtMost( + th.getDifference(stability_ETH_Difference, aliceETHGain), + 10000 + ); }); - it("provideToSP(), topup: tagged front end receives LQTY rewards", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); + it("withdrawFromSP(): All depositors are able to withdraw from the SP to their account", async () => { + // Whale opens trove + await openTrove({ ICR: toBN(dec(10, 18)), extraParams: { from: whale } }); - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); + // 1 defaulter open await openTrove({ - extraLUSDAmount: toBN(dec(200, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: defaulter_1 }, }); + + // 6 Accounts open troves and provide to SP + const depositors = [alice, bob, carol, dennis, erin, flyn]; + for (account of depositors) { + await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: account }, + }); + await stabilityPool.provideToSP(dec(10000, 18), { + from: account, + }); + } + + await priceFeed.setPrice(dec(105, 18)); + await troveManager.liquidate(defaulter_1); + + await priceFeed.setPrice(dec(200, 18)); + + // All depositors attempt to withdraw + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: alice }); + assert.equal((await stabilityPool.deposits(alice)).toString(), "0"); + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: bob }); + assert.equal((await stabilityPool.deposits(alice)).toString(), "0"); + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: carol }); + assert.equal((await stabilityPool.deposits(alice)).toString(), "0"); + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: dennis }); + assert.equal((await stabilityPool.deposits(alice)).toString(), "0"); + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: erin }); + assert.equal((await stabilityPool.deposits(alice)).toString(), "0"); + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: flyn }); + assert.equal((await stabilityPool.deposits(alice)).toString(), "0"); + + const totalDeposits = ( + await stabilityPool.getTotalBoldDeposits() + ).toString(); + + assert.isAtMost(th.getDifference(totalDeposits, "0"), 100000); + }); + + it("withdrawFromSP(): increases depositor's Bold token balance by the expected amount", async () => { + // Whale opens trove await openTrove({ - extraLUSDAmount: toBN(dec(300, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraBoldAmount: toBN(dec(100000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, }); - // A, B, C, provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: C }); + // 1 defaulter opens trove + await borrowerOperations.openTrove( + th._100pct, + await getOpenTroveBoldAmount(dec(10000, 18)), + defaulter_1, + defaulter_1, + { from: defaulter_1, value: dec(100, "ether") } + ); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider + const defaulterDebt = ( + await troveManager.getEntireDebtAndColl(defaulter_1) + )[0]; + + // 6 Accounts open troves and provide to SP + const depositors = [alice, bob, carol, dennis, erin, flyn]; + for (account of depositors) { + await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: account }, + }); + await stabilityPool.provideToSP(dec(10000, 18), { + from: account, + }); + } + + await priceFeed.setPrice(dec(105, 18)); + await troveManager.liquidate(defaulter_1); + + const aliceBalBefore = await boldToken.balanceOf(alice); + const bobBalBefore = await boldToken.balanceOf(bob); + + /* From an offset of 10000 Bold, each depositor receives + BoldLoss = 1666.6666666666666666 Bold + + and thus with a deposit of 10000 Bold, each should withdraw 8333.3333333333333333 Bold (in practice, slightly less due to rounding error) + */ + + // Price bounces back to $200 per ETH + await priceFeed.setPrice(dec(200, 18)); + + // Bob issues a further 5000 Bold from his trove + await borrowerOperations.withdrawBold( + th._100pct, + dec(5000, 18), + bob, + bob, + { from: bob } + ); + + // Expect Alice's Bold balance increase be very close to 8333.3333333333333333 Bold + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: alice }); + const aliceBalance = await boldToken.balanceOf(alice); + + assert.isAtMost( + th.getDifference( + aliceBalance.sub(aliceBalBefore), + "8333333333333333333333" + ), + 100000 ); - // Get front ends' LQTY balance before - const F1_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_1); - const F2_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_2); - const F3_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_3); - - // A, B, C top up (front end param passed here is irrelevant) - await stabilityPool.provideToSP(dec(10, 18), ZERO_ADDRESS, { from: A }); // provides no front end param - await stabilityPool.provideToSP(dec(20, 18), frontEnd_1, { from: B }); // provides front end that doesn't match his tag - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: C }); // provides front end that matches his tag - - // Get front ends' LQTY balance after - const F1_LQTYBalance_After = await lqtyToken.balanceOf(A); - const F2_LQTYBalance_After = await lqtyToken.balanceOf(B); - const F3_LQTYBalance_After = await lqtyToken.balanceOf(C); - - // Check LQTY Balance of front ends has increased - assert.isTrue(F1_LQTYBalance_After.gt(F1_LQTYBalance_Before)); - assert.isTrue(F2_LQTYBalance_After.gt(F2_LQTYBalance_Before)); - assert.isTrue(F3_LQTYBalance_After.gt(F3_LQTYBalance_Before)); + // expect Bob's Bold balance increase to be very close to 13333.33333333333333333 Bold + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: bob }); + const bobBalance = await boldToken.balanceOf(bob); + assert.isAtMost( + th.getDifference( + bobBalance.sub(bobBalBefore), + "13333333333333333333333" + ), + 100000 + ); }); - it("provideToSP(), topup: tagged front end's stake increases", async () => { + it("withdrawFromSP(): doesn't impact other users Stability deposits or ETH gains", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(100000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // A, B, C, D, E, F open troves + // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(200, 18)), + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(300, 18)), + extraBoldAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: carol }, }); - await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, + }); + await stabilityPool.provideToSP(dec(20000, 18), { + from: bob, + }); + await stabilityPool.provideToSP(dec(30000, 18), { + from: carol, }); + + // Would-be defaulters open troves await openTrove({ - extraLUSDAmount: toBN(dec(200, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: E }, + extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: toBN(dec(300, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: F }, + extraParams: { from: defaulter_2 }, }); - // A, B, C, D, E, F provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: C }); - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: D }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: E }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: F }); + // Price drops + await priceFeed.setPrice(dec(105, 18)); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider + // Defaulters are liquidated + await troveManager.liquidate(defaulter_1); + await troveManager.liquidate(defaulter_2); + assert.isFalse(await sortedTroves.contains(defaulter_1)); + assert.isFalse(await sortedTroves.contains(defaulter_2)); + + const alice_BoldDeposit_Before = ( + await stabilityPool.getCompoundedBoldDeposit(alice) + ).toString(); + const bob_BoldDeposit_Before = ( + await stabilityPool.getCompoundedBoldDeposit(bob) + ).toString(); + + const alice_ETHGain_Before = ( + await stabilityPool.getDepositorETHGain(alice) + ).toString(); + const bob_ETHGain_Before = ( + await stabilityPool.getDepositorETHGain(bob) + ).toString(); + + //check non-zero Bold and ETHGain in the Stability Pool + const BoldinSP = await stabilityPool.getTotalBoldDeposits(); + const ETHinSP = await stabilityPool.getETH(); + assert.isTrue(BoldinSP.gt(mv._zeroBN)); + assert.isTrue(ETHinSP.gt(mv._zeroBN)); + + // Price rises + await priceFeed.setPrice(dec(200, 18)); + + // Carol withdraws her Stability deposit + assert.equal( + (await stabilityPool.deposits(carol)).toString(), + dec(30000, 18) ); + await stabilityPool.withdrawFromSP(dec(30000, 18), { from: carol }); + assert.equal((await stabilityPool.deposits(carol)).toString(), "0"); + + const alice_BoldDeposit_After = ( + await stabilityPool.getCompoundedBoldDeposit(alice) + ).toString(); + const bob_BoldDeposit_After = ( + await stabilityPool.getCompoundedBoldDeposit(bob) + ).toString(); + + const alice_ETHGain_After = ( + await stabilityPool.getDepositorETHGain(alice) + ).toString(); + const bob_ETHGain_After = ( + await stabilityPool.getDepositorETHGain(bob) + ).toString(); + + // Check compounded deposits and ETH gains for A and B have not changed + assert.equal(alice_BoldDeposit_Before, alice_BoldDeposit_After); + assert.equal(bob_BoldDeposit_Before, bob_BoldDeposit_After); - // Get front ends' stake before - const F1_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_3); - - // A, B, C top up (front end param passed here is irrelevant) - await stabilityPool.provideToSP(dec(10, 18), ZERO_ADDRESS, { from: A }); // provides no front end param - await stabilityPool.provideToSP(dec(20, 18), frontEnd_1, { from: B }); // provides front end that doesn't match his tag - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: C }); // provides front end that matches his tag - - // Get front ends' stakes after - const F1_Stake_After = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_After = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_After = await stabilityPool.frontEndStakes(frontEnd_3); - - // Check front ends' stakes have increased - assert.isTrue(F1_Stake_After.gt(F1_Stake_Before)); - assert.isTrue(F2_Stake_After.gt(F2_Stake_Before)); - assert.isTrue(F3_Stake_After.gt(F3_Stake_Before)); + assert.equal(alice_ETHGain_Before, alice_ETHGain_After); + assert.equal(bob_ETHGain_Before, bob_ETHGain_After); }); - it("provideToSP(), topup: tagged front end's snapshots update", async () => { + it("withdrawFromSP(): doesn't impact system debt, collateral or TCR ", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(100000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // A, B, C, open troves + // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(200, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(400, 18)), + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(600, 18)), + extraBoldAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: carol }, }); - // D opens trove - await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, + }); + await stabilityPool.provideToSP(dec(20000, 18), { + from: bob, + }); + await stabilityPool.provideToSP(dec(30000, 18), { + from: carol, }); + // Would-be defaulters open troves await openTrove({ ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); + await openTrove({ + ICR: toBN(dec(2, 18)), + extraParams: { from: defaulter_2 }, + }); - // --- SETUP --- + // Price drops + await priceFeed.setPrice(dec(105, 18)); - const deposit_A = dec(100, 18); - const deposit_B = dec(200, 18); - const deposit_C = dec(300, 18); + // Defaulters are liquidated + await troveManager.liquidate(defaulter_1); + await troveManager.liquidate(defaulter_2); + assert.isFalse(await sortedTroves.contains(defaulter_1)); + assert.isFalse(await sortedTroves.contains(defaulter_2)); - // A, B, C make their initial deposits - await stabilityPool.provideToSP(deposit_A, frontEnd_1, { from: A }); - await stabilityPool.provideToSP(deposit_B, frontEnd_2, { from: B }); - await stabilityPool.provideToSP(deposit_C, frontEnd_3, { from: C }); + // Price rises + await priceFeed.setPrice(dec(200, 18)); - // fastforward time then make an SP deposit, to make G > 0 - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + const activeDebt_Before = (await activePool.getBoldDebt()).toString(); + const defaultedDebt_Before = (await defaultPool.getBoldDebt()).toString(); + const activeColl_Before = (await activePool.getETH()).toString(); + const defaultedColl_Before = (await defaultPool.getETH()).toString(); + const TCR_Before = (await th.getTCR(contracts)).toString(); - await stabilityPool.provideToSP( - await boldToken.balanceOf(D), - ZERO_ADDRESS, - { from: D } + // Carol withdraws her Stability deposit + assert.equal( + (await stabilityPool.deposits(carol)).toString(), + dec(30000, 18) ); + await stabilityPool.withdrawFromSP(dec(30000, 18), { from: carol }); + assert.equal((await stabilityPool.deposits(carol)).toString(), "0"); - // perform a liquidation to make 0 < P < 1, and S > 0 - await priceFeed.setPrice(dec(100, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); + const activeDebt_After = (await activePool.getBoldDebt()).toString(); + const defaultedDebt_After = (await defaultPool.getBoldDebt()).toString(); + const activeColl_After = (await activePool.getETH()).toString(); + const defaultedColl_After = (await defaultPool.getETH()).toString(); + const TCR_After = (await th.getTCR(contracts)).toString(); - await troveManager.liquidate(defaulter_1); + // Check total system debt, collateral and TCR have not changed after a Stability deposit is made + assert.equal(activeDebt_Before, activeDebt_After); + assert.equal(defaultedDebt_Before, defaultedDebt_After); + assert.equal(activeColl_Before, activeColl_After); + assert.equal(defaultedColl_Before, defaultedColl_After); + assert.equal(TCR_Before, TCR_After); + }); - const currentEpoch = await stabilityPool.currentEpoch(); - const currentScale = await stabilityPool.currentScale(); + it("withdrawFromSP(): doesn't impact any troves, including the caller's trove", async () => { + await openTrove({ + extraBoldAmount: toBN(dec(100000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, + }); - const S_Before = await stabilityPool.epochToScaleToSum( - currentEpoch, - currentScale - ); - const P_Before = await stabilityPool.P(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); + // A, B, C open troves and make Stability Pool deposits + await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: alice }, + }); + await openTrove({ + extraBoldAmount: toBN(dec(20000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: bob }, + }); + await openTrove({ + extraBoldAmount: toBN(dec(30000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: carol }, + }); - // Confirm 0 < P < 1 - assert.isTrue(P_Before.gt(toBN("0")) && P_Before.lt(toBN(dec(1, 18)))); - // Confirm S, G are both > 0 - assert.isTrue(S_Before.gt(toBN("0"))); - assert.isTrue(G_Before.gt(toBN("0"))); + // A, B and C provide to SP + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, + }); + await stabilityPool.provideToSP(dec(20000, 18), { + from: bob, + }); + await stabilityPool.provideToSP(dec(30000, 18), { + from: carol, + }); - // Get front ends' snapshots before - for (frontEnd of [frontEnd_1, frontEnd_2, frontEnd_3]) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); + // Price drops + await priceFeed.setPrice(dec(105, 18)); + const price = await priceFeed.getPrice(); - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends, since S corresponds to ETH gain) - assert.equal(snapshot[1], dec(1, 18)); // P - assert.equal(snapshot[2], "0"); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } + // Get debt, collateral and ICR of all existing troves + const whale_Debt_Before = ( + await troveManager.Troves(whale) + )[0].toString(); + const alice_Debt_Before = ( + await troveManager.Troves(alice) + )[0].toString(); + const bob_Debt_Before = (await troveManager.Troves(bob))[0].toString(); + const carol_Debt_Before = ( + await troveManager.Troves(carol) + )[0].toString(); - // --- TEST --- + const whale_Coll_Before = ( + await troveManager.Troves(whale) + )[1].toString(); + const alice_Coll_Before = ( + await troveManager.Troves(alice) + )[1].toString(); + const bob_Coll_Before = (await troveManager.Troves(bob))[1].toString(); + const carol_Coll_Before = ( + await troveManager.Troves(carol) + )[1].toString(); - // A, B, C top up their deposits. Grab G at each stage, as it can increase a bit - // between topups, because some block.timestamp time passes (and LQTY is issued) between ops - const G1 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.provideToSP(deposit_A, frontEnd_1, { from: A }); + const whale_ICR_Before = ( + await troveManager.getCurrentICR(whale, price) + ).toString(); + const alice_ICR_Before = ( + await troveManager.getCurrentICR(alice, price) + ).toString(); + const bob_ICR_Before = ( + await troveManager.getCurrentICR(bob, price) + ).toString(); + const carol_ICR_Before = ( + await troveManager.getCurrentICR(carol, price) + ).toString(); - const G2 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.provideToSP(deposit_B, frontEnd_2, { from: B }); + // price rises + await priceFeed.setPrice(dec(200, 18)); - const G3 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch + // Carol withdraws her Stability deposit + assert.equal( + (await stabilityPool.deposits(carol)).toString(), + dec(30000, 18) ); - await stabilityPool.provideToSP(deposit_C, frontEnd_3, { from: C }); + await stabilityPool.withdrawFromSP(dec(30000, 18), { from: carol }); + assert.equal((await stabilityPool.deposits(carol)).toString(), "0"); - const frontEnds = [frontEnd_1, frontEnd_2, frontEnd_3]; - const G_Values = [G1, G2, G3]; + const whale_Debt_After = (await troveManager.Troves(whale))[0].toString(); + const alice_Debt_After = (await troveManager.Troves(alice))[0].toString(); + const bob_Debt_After = (await troveManager.Troves(bob))[0].toString(); + const carol_Debt_After = (await troveManager.Troves(carol))[0].toString(); - // Map frontEnds to the value of G at time the deposit was made - frontEndToG = th.zipToObject(frontEnds, G_Values); + const whale_Coll_After = (await troveManager.Troves(whale))[1].toString(); + const alice_Coll_After = (await troveManager.Troves(alice))[1].toString(); + const bob_Coll_After = (await troveManager.Troves(bob))[1].toString(); + const carol_Coll_After = (await troveManager.Troves(carol))[1].toString(); - // Get front ends' snapshots after - for (const [frontEnd, G] of Object.entries(frontEndToG)) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); + const whale_ICR_After = ( + await troveManager.getCurrentICR(whale, price) + ).toString(); + const alice_ICR_After = ( + await troveManager.getCurrentICR(alice, price) + ).toString(); + const bob_ICR_After = ( + await troveManager.getCurrentICR(bob, price) + ).toString(); + const carol_ICR_After = ( + await troveManager.getCurrentICR(carol, price) + ).toString(); - // Check snapshots are the expected values - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends) - assert.isTrue(snapshot[1].eq(P_Before)); // P - assert.isTrue(snapshot[2].eq(G)); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } + // Check all troves are unaffected by Carol's Stability deposit withdrawal + assert.equal(whale_Debt_Before, whale_Debt_After); + assert.equal(alice_Debt_Before, alice_Debt_After); + assert.equal(bob_Debt_Before, bob_Debt_After); + assert.equal(carol_Debt_Before, carol_Debt_After); + + assert.equal(whale_Coll_Before, whale_Coll_After); + assert.equal(alice_Coll_Before, alice_Coll_After); + assert.equal(bob_Coll_Before, bob_Coll_After); + assert.equal(carol_Coll_Before, carol_Coll_After); + + assert.equal(whale_ICR_Before, whale_ICR_After); + assert.equal(alice_ICR_Before, alice_ICR_After); + assert.equal(bob_ICR_Before, bob_ICR_After); + assert.equal(carol_ICR_Before, carol_ICR_After); }); - it("provideToSP(): reverts when amount is zero", async () => { + it("withdrawFromSP(): succeeds when amount is 0 and system has an undercollateralized trove", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, + extraBoldAmount: toBN(dec(100, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: A }, }); + await stabilityPool.provideToSP(dec(100, 18), { from: A }); + + const A_initialDeposit = (await stabilityPool.deposits(A)).toString(); + assert.equal(A_initialDeposit, dec(100, 18)); + + // defaulters opens trove await openTrove({ - extraLUSDAmount: toBN(dec(1000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: defaulter_1 }, }); await openTrove({ - extraLUSDAmount: toBN(dec(2000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: defaulter_2 }, }); - // Whale transfers LUSD to C, D - await boldToken.transfer(C, dec(100, 18), { from: whale }); - await boldToken.transfer(D, dec(100, 18), { from: whale }); - - txPromise_A = stabilityPool.provideToSP(0, frontEnd_1, { from: A }); - txPromise_B = stabilityPool.provideToSP(0, ZERO_ADDRESS, { from: B }); - txPromise_C = stabilityPool.provideToSP(0, frontEnd_2, { from: C }); - txPromise_D = stabilityPool.provideToSP(0, ZERO_ADDRESS, { from: D }); - - await th.assertRevert( - txPromise_A, - "StabilityPool: Amount must be non-zero" + // ETH drops, defaulters are in liquidation range + await priceFeed.setPrice(dec(105, 18)); + const price = await priceFeed.getPrice(); + assert.isTrue( + await th.ICRbetween100and110(defaulter_1, troveManager, price) ); - await th.assertRevert( - txPromise_B, - "StabilityPool: Amount must be non-zero" + + await th.fastForwardTime( + timeValues.MINUTES_IN_ONE_WEEK, + web3.currentProvider ); - await th.assertRevert( - txPromise_C, - "StabilityPool: Amount must be non-zero" + + // Liquidate d1 + await troveManager.liquidate(defaulter_1); + assert.isFalse(await sortedTroves.contains(defaulter_1)); + + // Check d2 is undercollateralized + assert.isTrue( + await th.ICRbetween100and110(defaulter_2, troveManager, price) ); - await th.assertRevert( - txPromise_D, - "StabilityPool: Amount must be non-zero" + assert.isTrue(await sortedTroves.contains(defaulter_2)); + + const A_ETHBalBefore = toBN(await web3.eth.getBalance(A)); + + // Check Alice has gains to withdraw + const A_pendingETHGain = await stabilityPool.getDepositorETHGain(A); + assert.isTrue(A_pendingETHGain.gt(toBN("0"))); + + // Check withdrawal of 0 succeeds + const tx = await stabilityPool.withdrawFromSP(0, { + from: A, + gasPrice: GAS_PRICE, + }); + assert.isTrue(tx.receipt.status); + + const A_expectedBalance = A_ETHBalBefore.sub( + toBN(th.gasUsed(tx) * GAS_PRICE) ); + + const A_ETHBalAfter = toBN(await web3.eth.getBalance(A)); + + // Check A's ETH balance has increased correctly + assert.isTrue(A_ETHBalAfter.sub(A_expectedBalance).eq(A_pendingETHGain)); }); - it("provideToSP(): reverts if user is a registered front end", async () => { - // C, D, E, F open troves + it("withdrawFromSP(): withdrawing 0 Bold doesn't alter the caller's deposit or the total Bold in the Stability Pool", async () => { + // --- SETUP --- await openTrove({ - extraLUSDAmount: toBN(dec(30, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, }); + + // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(30, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(30, 18)), + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: E }, + extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(30, 18)), + extraBoldAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: F }, + extraParams: { from: carol }, }); - // C, E, F registers as front end - await stabilityPool.registerFrontEnd(dec(1, 18), { from: C }); - await stabilityPool.registerFrontEnd(dec(1, 18), { from: E }); - await stabilityPool.registerFrontEnd(dec(1, 18), { from: F }); - - const txPromise_C = stabilityPool.provideToSP(dec(10, 18), ZERO_ADDRESS, { - from: C, - }); - const txPromise_E = stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { - from: E, - }); - const txPromise_F = stabilityPool.provideToSP(dec(10, 18), F, { - from: F, + // A, B, C provides 100, 50, 30 Bold to SP + await stabilityPool.provideToSP(dec(100, 18), { + from: alice, }); - await th.assertRevert( - txPromise_C, - "StabilityPool: must not already be a registered front end" - ); - await th.assertRevert( - txPromise_E, - "StabilityPool: must not already be a registered front end" - ); - await th.assertRevert( - txPromise_F, - "StabilityPool: must not already be a registered front end" - ); + await stabilityPool.provideToSP(dec(50, 18), { from: bob }); + await stabilityPool.provideToSP(dec(30, 18), { from: carol }); - const txD = await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { - from: D, - }); - assert.isTrue(txD.receipt.status); + const bob_Deposit_Before = ( + await stabilityPool.getCompoundedBoldDeposit(bob) + ).toString(); + const BoldinSP_Before = ( + await stabilityPool.getTotalBoldDeposits() + ).toString(); + + assert.equal(BoldinSP_Before, dec(180, 18)); + + // Bob withdraws 0 Bold from the Stability Pool + await stabilityPool.withdrawFromSP(0, { from: bob }); + + // check Bob's deposit and total Bold in Stability Pool has not changed + const bob_Deposit_After = ( + await stabilityPool.getCompoundedBoldDeposit(bob) + ).toString(); + const BoldinSP_After = ( + await stabilityPool.getTotalBoldDeposits() + ).toString(); + + assert.equal(bob_Deposit_Before, bob_Deposit_After); + assert.equal(BoldinSP_Before, BoldinSP_After); }); - it("provideToSP(): reverts if provided tag is not a registered front end", async () => { + it("withdrawFromSP(): withdrawing 0 ETH Gain does not alter the caller's ETH balance, their trove collateral, or the ETH in the Stability Pool", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(30, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, }); + + // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(30, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(30, 18)), + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: E }, + extraParams: { from: bob }, }); - - const txPromise_C = stabilityPool.provideToSP(dec(10, 18), A, { - from: C, - }); // passes another EOA - const txPromise_D = stabilityPool.provideToSP( - dec(10, 18), - troveManager.address, - { from: D } - ); - const txPromise_E = stabilityPool.provideToSP( - dec(10, 18), - stabilityPool.address, - { from: E } - ); - const txPromise_F = stabilityPool.provideToSP(dec(10, 18), F, { - from: F, - }); // passes itself - - await th.assertRevert( - txPromise_C, - "StabilityPool: Tag must be a registered front end, or the zero address" - ); - await th.assertRevert( - txPromise_D, - "StabilityPool: Tag must be a registered front end, or the zero address" - ); - await th.assertRevert( - txPromise_E, - "StabilityPool: Tag must be a registered front end, or the zero address" - ); - await th.assertRevert( - txPromise_F, - "StabilityPool: Tag must be a registered front end, or the zero address" - ); - }); - - // --- withdrawFromSP --- - - it("withdrawFromSP(): reverts when user has no active deposit", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), + extraBoldAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, + extraParams: { from: carol }, }); + + // Would-be defaulter open trove await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { - from: alice, + extraParams: { from: defaulter_1 }, }); - const alice_initialDeposit = ( - await stabilityPool.deposits(alice) - )[0].toString(); - const bob_initialDeposit = ( - await stabilityPool.deposits(bob) - )[0].toString(); - - assert.equal(alice_initialDeposit, dec(100, 18)); - assert.equal(bob_initialDeposit, "0"); + // Price drops + await priceFeed.setPrice(dec(105, 18)); - const txAlice = await stabilityPool.withdrawFromSP(dec(100, 18), { - from: alice, - }); - assert.isTrue(txAlice.receipt.status); + assert.isFalse(await th.checkRecoveryMode(contracts)); - try { - const txBob = await stabilityPool.withdrawFromSP(dec(100, 18), { - from: bob, - }); - assert.isFalse(txBob.receipt.status); - } catch (err) { - assert.include(err.message, "revert"); - // TODO: infamous issue #99 - //assert.include(err.message, "User must have a non-zero deposit") - } - }); + // Defaulter 1 liquidated, full offset + await troveManager.liquidate(defaulter_1); - it("withdrawFromSP(): reverts when amount > 0 and system has an undercollateralized trove", async () => { + // Dennis opens trove and deposits to Stability Pool await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, + extraParams: { from: dennis }, }); - - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { - from: alice, + await stabilityPool.provideToSP(dec(100, 18), { + from: dennis, }); - const alice_initialDeposit = ( - await stabilityPool.deposits(alice) - )[0].toString(); - assert.equal(alice_initialDeposit, dec(100, 18)); + // Check Dennis has 0 ETHGain + const dennis_ETHGain = ( + await stabilityPool.getDepositorETHGain(dennis) + ).toString(); + assert.equal(dennis_ETHGain, "0"); - // defaulter opens trove - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + const dennis_ETHBalance_Before = web3.eth.getBalance(dennis).toString(); + const dennis_Collateral_Before = ( + await troveManager.Troves(dennis) + )[1].toString(); + const ETHinSP_Before = (await stabilityPool.getETH()).toString(); + + await priceFeed.setPrice(dec(200, 18)); + + // Dennis withdraws his full deposit and ETHGain to his account + await stabilityPool.withdrawFromSP(dec(100, 18), { + from: dennis, + gasPrice: GAS_PRICE, }); - // ETH drops, defaulter is in liquidation range (but not liquidated yet) - await priceFeed.setPrice(dec(100, 18)); + // Check withdrawal does not alter Dennis' ETH balance or his trove's collateral + const dennis_ETHBalance_After = web3.eth.getBalance(dennis).toString(); + const dennis_Collateral_After = ( + await troveManager.Troves(dennis) + )[1].toString(); + const ETHinSP_After = (await stabilityPool.getETH()).toString(); - await th.assertRevert( - stabilityPool.withdrawFromSP(dec(100, 18), { from: alice }) - ); + assert.equal(dennis_ETHBalance_Before, dennis_ETHBalance_After); + assert.equal(dennis_Collateral_Before, dennis_Collateral_After); + + // Check withdrawal has not altered the ETH in the Stability Pool + assert.equal(ETHinSP_Before, ETHinSP_After); }); - it("withdrawFromSP(): partial retrieval - retrieves correct LUSD amount and the entire ETH Gain, and updates deposit", async () => { + it("withdrawFromSP(): Request to withdraw > caller's deposit only withdraws the caller's compounded deposit", async () => { // --- SETUP --- - // Whale deposits 185000 LUSD in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(1, 24)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); - // 2 Troves opened + // A, B, C open troves and make Stability Pool deposits await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + extraParams: { from: alice }, }); await openTrove({ + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, + extraParams: { from: bob }, + }); + await openTrove({ + extraBoldAmount: toBN(dec(30000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: carol }, }); - // --- TEST --- - - // Alice makes deposit #1: 15000 LUSD await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: alice }, + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: defaulter_1 }, }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { + + // A, B, C provide Bold to SP + await stabilityPool.provideToSP(dec(10000, 18), { from: alice, }); + await stabilityPool.provideToSP(dec(20000, 18), { + from: bob, + }); + await stabilityPool.provideToSP(dec(30000, 18), { + from: carol, + }); - // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active + // Price drops await priceFeed.setPrice(dec(105, 18)); - // 2 users with Trove with 170 LUSD drawn are closed - const liquidationTX_1 = await troveManager.liquidate(defaulter_1, { - from: owner, - }); // 170 LUSD closed - const liquidationTX_2 = await troveManager.liquidate(defaulter_2, { - from: owner, - }); // 170 LUSD closed + // Liquidate defaulter 1 + await troveManager.liquidate(defaulter_1); - const [liquidatedDebt_1] = await th.getEmittedLiquidationValues( - liquidationTX_1 + const alice_Bold_Balance_Before = await boldToken.balanceOf(alice); + const bob_Bold_Balance_Before = await boldToken.balanceOf(bob); + + const alice_Deposit_Before = await stabilityPool.getCompoundedBoldDeposit( + alice ); - const [liquidatedDebt_2] = await th.getEmittedLiquidationValues( - liquidationTX_2 + const bob_Deposit_Before = await stabilityPool.getCompoundedBoldDeposit( + bob ); - // Alice LUSDLoss is ((15000/200000) * liquidatedDebt), for each liquidation - const expectedLUSDLoss_A = liquidatedDebt_1 - .mul(toBN(dec(15000, 18))) - .div(toBN(dec(200000, 18))) - .add( - liquidatedDebt_2.mul(toBN(dec(15000, 18))).div(toBN(dec(200000, 18))) - ); + const BoldinSP_Before = await stabilityPool.getTotalBoldDeposits(); - const expectedCompoundedLUSDDeposit_A = toBN(dec(15000, 18)).sub( - expectedLUSDLoss_A - ); - const compoundedLUSDDeposit_A = - await stabilityPool.getCompoundedLUSDDeposit(alice); + await priceFeed.setPrice(dec(200, 18)); - assert.isAtMost( - th.getDifference( - expectedCompoundedLUSDDeposit_A, - compoundedLUSDDeposit_A - ), - 100000 - ); + // Bob attempts to withdraws 1 wei more than his compounded deposit from the Stability Pool + await stabilityPool.withdrawFromSP(bob_Deposit_Before.add(toBN(1)), { + from: bob, + }); - // Alice retrieves part of her entitled LUSD: 9000 LUSD - await stabilityPool.withdrawFromSP(dec(9000, 18), { from: alice }); + // Check Bob's Bold balance has risen by only the value of his compounded deposit + const bob_expectedBoldBalance = bob_Bold_Balance_Before + .add(bob_Deposit_Before) + .toString(); + const bob_Bold_Balance_After = ( + await boldToken.balanceOf(bob) + ).toString(); + assert.equal(bob_Bold_Balance_After, bob_expectedBoldBalance); - const expectedNewDeposit_A = compoundedLUSDDeposit_A.sub( - toBN(dec(9000, 18)) - ); + // Alice attempts to withdraws 2309842309.000000000000000000 Bold from the Stability Pool + await stabilityPool.withdrawFromSP("2309842309000000000000000000", { + from: alice, + }); - // check Alice's deposit has been updated to equal her compounded deposit minus her withdrawal */ - const newDeposit = (await stabilityPool.deposits(alice))[0].toString(); - assert.isAtMost( - th.getDifference(newDeposit, expectedNewDeposit_A), - 100000 - ); + // Check Alice's Bold balance has risen by only the value of her compounded deposit + const alice_expectedBoldBalance = alice_Bold_Balance_Before + .add(alice_Deposit_Before) + .toString(); + const alice_Bold_Balance_After = ( + await boldToken.balanceOf(alice) + ).toString(); + assert.equal(alice_Bold_Balance_After, alice_expectedBoldBalance); - // Expect Alice has withdrawn all ETH gain - const alice_pendingETHGain = await stabilityPool.getDepositorETHGain( - alice - ); - assert.equal(alice_pendingETHGain, 0); + // Check Bold in Stability Pool has been reduced by only Alice's compounded deposit and Bob's compounded deposit + const expectedBoldinSP = BoldinSP_Before.sub(alice_Deposit_Before) + .sub(bob_Deposit_Before) + .toString(); + const BoldinSP_After = ( + await stabilityPool.getTotalBoldDeposits() + ).toString(); + assert.equal(BoldinSP_After, expectedBoldinSP); }); - it("withdrawFromSP(): partial retrieval - leaves the correct amount of LUSD in the Stability Pool", async () => { + it("withdrawFromSP(): Request to withdraw 2^256-1 Bold only withdraws the caller's compounded deposit", async () => { // --- SETUP --- - // Whale deposits 185000 LUSD in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(1, 24)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); - // 2 Troves opened + // A, B, C open troves + // A, B, C open troves + // A, B, C open troves + // A, B, C open troves + // A, B, C open troves + // A, B, C open troves + // A, B, C open troves await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + extraParams: { from: alice }, }); await openTrove({ + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, + extraParams: { from: bob }, + }); + await openTrove({ + extraBoldAmount: toBN(dec(30000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: carol }, }); - // --- TEST --- - // Alice makes deposit #1: 15000 LUSD await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: alice }, + ICR: toBN(dec(2, 18)), + extraParams: { from: defaulter_1 }, }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { + + // A, B, C provides 100, 50, 30 Bold to SP + await stabilityPool.provideToSP(dec(100, 18), { from: alice, }); + await stabilityPool.provideToSP(dec(50, 18), { from: bob }); + await stabilityPool.provideToSP(dec(30, 18), { from: carol }); - const SP_LUSD_Before = await stabilityPool.getTotalLUSDDeposits(); - assert.equal(SP_LUSD_Before, dec(200000, 18)); + // Price drops + await priceFeed.setPrice(dec(100, 18)); - // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active - await priceFeed.setPrice(dec(105, 18)); + // Liquidate defaulter 1 + await troveManager.liquidate(defaulter_1); - // 2 users liquidated - const liquidationTX_1 = await troveManager.liquidate(defaulter_1, { - from: owner, - }); - const liquidationTX_2 = await troveManager.liquidate(defaulter_2, { - from: owner, - }); + const bob_Bold_Balance_Before = await boldToken.balanceOf(bob); - const [liquidatedDebt_1] = await th.getEmittedLiquidationValues( - liquidationTX_1 + const bob_Deposit_Before = await stabilityPool.getCompoundedBoldDeposit( + bob ); - const [liquidatedDebt_2] = await th.getEmittedLiquidationValues( - liquidationTX_2 + + const BoldinSP_Before = await stabilityPool.getTotalBoldDeposits(); + + const maxBytes32 = web3.utils.toBN( + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ); - // Alice retrieves part of her entitled LUSD: 9000 LUSD - await stabilityPool.withdrawFromSP(dec(9000, 18), { from: alice }); + // Price drops + await priceFeed.setPrice(dec(200, 18)); - /* Check SP has reduced from 2 liquidations and Alice's withdrawal - Expect LUSD in SP = (200000 - liquidatedDebt_1 - liquidatedDebt_2 - 9000) */ - const expectedSPLUSD = toBN(dec(200000, 18)) - .sub(toBN(liquidatedDebt_1)) - .sub(toBN(liquidatedDebt_2)) - .sub(toBN(dec(9000, 18))); + // Bob attempts to withdraws maxBytes32 Bold from the Stability Pool + await stabilityPool.withdrawFromSP(maxBytes32, { from: bob }); - const SP_LUSD_After = ( - await stabilityPool.getTotalLUSDDeposits() + // Check Bob's Bold balance has risen by only the value of his compounded deposit + const bob_expectedBoldBalance = bob_Bold_Balance_Before + .add(bob_Deposit_Before) + .toString(); + const bob_Bold_Balance_After = ( + await boldToken.balanceOf(bob) ).toString(); + assert.equal(bob_Bold_Balance_After, bob_expectedBoldBalance); - th.assertIsApproximatelyEqual(SP_LUSD_After, expectedSPLUSD); + // Check Bold in Stability Pool has been reduced by only Bob's compounded deposit + const expectedBoldinSP = + BoldinSP_Before.sub(bob_Deposit_Before).toString(); + const BoldinSP_After = ( + await stabilityPool.getTotalBoldDeposits() + ).toString(); + assert.equal(BoldinSP_After, expectedBoldinSP); }); - it("withdrawFromSP(): full retrieval - leaves the correct amount of LUSD in the Stability Pool", async () => { + it("withdrawFromSP(): caller can withdraw full deposit and ETH gain during Recovery Mode", async () => { // --- SETUP --- - // Whale deposits 185000 LUSD in StabilityPool + + // Price doubles + await priceFeed.setPrice(dec(400, 18)); await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), + ICR: toBN(dec(2, 18)), extraParams: { from: whale }, }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); - - // 2 Troves opened - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, - }); - - // --- TEST --- - - // Alice makes deposit #1 - await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: alice }, - }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { - from: alice, - }); - - const SP_LUSD_Before = await stabilityPool.getTotalLUSDDeposits(); - assert.equal(SP_LUSD_Before, dec(200000, 18)); - - // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active - await priceFeed.setPrice(dec(105, 18)); - - // 2 defaulters liquidated - const liquidationTX_1 = await troveManager.liquidate(defaulter_1, { - from: owner, - }); - const liquidationTX_2 = await troveManager.liquidate(defaulter_2, { - from: owner, - }); - - const [liquidatedDebt_1] = await th.getEmittedLiquidationValues( - liquidationTX_1 - ); - const [liquidatedDebt_2] = await th.getEmittedLiquidationValues( - liquidationTX_2 - ); - - // Alice LUSDLoss is ((15000/200000) * liquidatedDebt), for each liquidation - const expectedLUSDLoss_A = liquidatedDebt_1 - .mul(toBN(dec(15000, 18))) - .div(toBN(dec(200000, 18))) - .add( - liquidatedDebt_2.mul(toBN(dec(15000, 18))).div(toBN(dec(200000, 18))) - ); - - const expectedCompoundedLUSDDeposit_A = toBN(dec(15000, 18)).sub( - expectedLUSDLoss_A - ); - const compoundedLUSDDeposit_A = - await stabilityPool.getCompoundedLUSDDeposit(alice); - - assert.isAtMost( - th.getDifference( - expectedCompoundedLUSDDeposit_A, - compoundedLUSDDeposit_A - ), - 100000 - ); - - const LUSDinSPBefore = await stabilityPool.getTotalLUSDDeposits(); - - // Alice retrieves all of her entitled LUSD: - await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); - - const expectedLUSDinSPAfter = LUSDinSPBefore.sub(compoundedLUSDDeposit_A); - - const LUSDinSPAfter = await stabilityPool.getTotalLUSDDeposits(); - assert.isAtMost( - th.getDifference(expectedLUSDinSPAfter, LUSDinSPAfter), - 100000 - ); - }); - - it("withdrawFromSP(): Subsequent deposit and withdrawal attempt from same account, with no intermediate liquidations, withdraws zero ETH", async () => { - // --- SETUP --- - // Whale deposits 1850 LUSD in StabilityPool - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - await stabilityPool.provideToSP(dec(18500, 18), frontEnd_1, { - from: whale, - }); - - // 2 defaulters open - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, - }); - - // --- TEST --- - - // Alice makes deposit #1: 15000 LUSD - await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: alice }, - }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { - from: alice, - }); - - // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active - await priceFeed.setPrice(dec(105, 18)); - - // defaulters liquidated - await troveManager.liquidate(defaulter_1, { from: owner }); - await troveManager.liquidate(defaulter_2, { from: owner }); - - // Alice retrieves all of her entitled LUSD: - await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); - assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - - // Alice makes second deposit - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - - const ETHinSP_Before = (await stabilityPool.getETH()).toString(); - - // Alice attempts second withdrawal - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: alice }); - assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - - // Check ETH in pool does not change - const ETHinSP_1 = (await stabilityPool.getETH()).toString(); - assert.equal(ETHinSP_Before, ETHinSP_1); - - // Third deposit - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - - // Alice attempts third withdrawal (this time, frm SP to Trove) - const txPromise_A = stabilityPool.withdrawETHGainToTrove(alice, alice, { - from: alice, - }); - await th.assertRevert(txPromise_A); - }); - - it("withdrawFromSP(): it correctly updates the user's LUSD and ETH snapshots of entitled reward per unit staked", async () => { - // --- SETUP --- - // Whale deposits 185000 LUSD in StabilityPool - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); - - // 2 defaulters open - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, - }); - - // --- TEST --- - - // Alice makes deposit #1: 15000 LUSD - await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: alice }, - }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { - from: alice, - }); - - // check 'Before' snapshots - const alice_snapshot_Before = await stabilityPool.depositSnapshots(alice); - const alice_snapshot_S_Before = alice_snapshot_Before[0].toString(); - const alice_snapshot_P_Before = alice_snapshot_Before[1].toString(); - assert.equal(alice_snapshot_S_Before, 0); - assert.equal(alice_snapshot_P_Before, "1000000000000000000"); - - // price drops: defaulters' Troves fall below MCR, alice and whale Trove remain active - await priceFeed.setPrice(dec(105, 18)); - - // 2 defaulters liquidated - await troveManager.liquidate(defaulter_1, { from: owner }); - await troveManager.liquidate(defaulter_2, { from: owner }); - - // Alice retrieves part of her entitled LUSD: 9000 LUSD - await stabilityPool.withdrawFromSP(dec(9000, 18), { from: alice }); - - const P = (await stabilityPool.P()).toString(); - const S = (await stabilityPool.epochToScaleToSum(0, 0)).toString(); - // check 'After' snapshots - const alice_snapshot_After = await stabilityPool.depositSnapshots(alice); - const alice_snapshot_S_After = alice_snapshot_After[0].toString(); - const alice_snapshot_P_After = alice_snapshot_After[1].toString(); - assert.equal(alice_snapshot_S_After, S); - assert.equal(alice_snapshot_P_After, P); - }); - - it("withdrawFromSP(): decreases StabilityPool ETH", async () => { - // --- SETUP --- - // Whale deposits 185000 LUSD in StabilityPool - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); - - // 1 defaulter opens - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // --- TEST --- - - // Alice makes deposit #1: 15000 LUSD - await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: alice }, - }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { - from: alice, - }); - - // price drops: defaulter's Trove falls below MCR, alice and whale Trove remain active - await priceFeed.setPrice("100000000000000000000"); - - // defaulter's Trove is closed. - const liquidationTx_1 = await troveManager.liquidate(defaulter_1, { - from: owner, - }); // 180 LUSD closed - const [, liquidatedColl] = - th.getEmittedLiquidationValues(liquidationTx_1); - - //Get ActivePool and StabilityPool Ether before retrieval: - const active_ETH_Before = await activePool.getETH(); - const stability_ETH_Before = await stabilityPool.getETH(); - - // Expect alice to be entitled to 15000/200000 of the liquidated coll - const aliceExpectedETHGain = liquidatedColl - .mul(toBN(dec(15000, 18))) - .div(toBN(dec(200000, 18))); - const aliceETHGain = await stabilityPool.getDepositorETHGain(alice); - assert.isTrue(aliceExpectedETHGain.eq(aliceETHGain)); - - // Alice retrieves all of her deposit - await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); - - const active_ETH_After = await activePool.getETH(); - const stability_ETH_After = await stabilityPool.getETH(); - - const active_ETH_Difference = active_ETH_Before.sub(active_ETH_After); - const stability_ETH_Difference = - stability_ETH_Before.sub(stability_ETH_After); - - assert.equal(active_ETH_Difference, "0"); - - // Expect StabilityPool to have decreased by Alice's ETHGain - assert.isAtMost( - th.getDifference(stability_ETH_Difference, aliceETHGain), - 10000 - ); - }); - - it("withdrawFromSP(): All depositors are able to withdraw from the SP to their account", async () => { - // Whale opens trove - await openTrove({ ICR: toBN(dec(10, 18)), extraParams: { from: whale } }); - - // 1 defaulter open - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // 6 Accounts open troves and provide to SP - const depositors = [alice, bob, carol, dennis, erin, flyn]; - for (account of depositors) { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: account }, - }); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: account, - }); - } - - await priceFeed.setPrice(dec(105, 18)); - await troveManager.liquidate(defaulter_1); - - await priceFeed.setPrice(dec(200, 18)); - - // All depositors attempt to withdraw - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: alice }); - assert.equal((await stabilityPool.deposits(alice))[0].toString(), "0"); - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: bob }); - assert.equal((await stabilityPool.deposits(alice))[0].toString(), "0"); - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: carol }); - assert.equal((await stabilityPool.deposits(alice))[0].toString(), "0"); - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: dennis }); - assert.equal((await stabilityPool.deposits(alice))[0].toString(), "0"); - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: erin }); - assert.equal((await stabilityPool.deposits(alice))[0].toString(), "0"); - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: flyn }); - assert.equal((await stabilityPool.deposits(alice))[0].toString(), "0"); - - const totalDeposits = ( - await stabilityPool.getTotalLUSDDeposits() - ).toString(); - - assert.isAtMost(th.getDifference(totalDeposits, "0"), 100000); - }); - - it("withdrawFromSP(): increases depositor's LUSD token balance by the expected amount", async () => { - // Whale opens trove - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // 1 defaulter opens trove - await borrowerOperations.openTrove( - th._100pct, - await getOpenTroveLUSDAmount(dec(10000, 18)), - defaulter_1, - defaulter_1, - { from: defaulter_1, value: dec(100, "ether") } - ); - - const defaulterDebt = ( - await troveManager.getEntireDebtAndColl(defaulter_1) - )[0]; - - // 6 Accounts open troves and provide to SP - const depositors = [alice, bob, carol, dennis, erin, flyn]; - for (account of depositors) { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: account }, - }); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: account, - }); - } - - await priceFeed.setPrice(dec(105, 18)); - await troveManager.liquidate(defaulter_1); - - const aliceBalBefore = await boldToken.balanceOf(alice); - const bobBalBefore = await boldToken.balanceOf(bob); - - /* From an offset of 10000 LUSD, each depositor receives - LUSDLoss = 1666.6666666666666666 LUSD - - and thus with a deposit of 10000 LUSD, each should withdraw 8333.3333333333333333 LUSD (in practice, slightly less due to rounding error) - */ - - // Price bounces back to $200 per ETH - await priceFeed.setPrice(dec(200, 18)); - - // Bob issues a further 5000 LUSD from his trove - await borrowerOperations.withdrawLUSD( - th._100pct, - dec(5000, 18), - bob, - bob, - { from: bob } - ); - - // Expect Alice's LUSD balance increase be very close to 8333.3333333333333333 LUSD - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: alice }); - const aliceBalance = await boldToken.balanceOf(alice); - - assert.isAtMost( - th.getDifference( - aliceBalance.sub(aliceBalBefore), - "8333333333333333333333" - ), - 100000 - ); - - // expect Bob's LUSD balance increase to be very close to 13333.33333333333333333 LUSD - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: bob }); - const bobBalance = await boldToken.balanceOf(bob); - assert.isAtMost( - th.getDifference( - bobBalance.sub(bobBalBefore), - "13333333333333333333333" - ), - 100000 - ); - }); - - it("withdrawFromSP(): doesn't impact other users Stability deposits or ETH gains", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - await stabilityPool.provideToSP(dec(20000, 18), frontEnd_1, { - from: bob, - }); - await stabilityPool.provideToSP(dec(30000, 18), frontEnd_1, { - from: carol, - }); - - // Would-be defaulters open troves - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, - }); - - // Price drops - await priceFeed.setPrice(dec(105, 18)); - - // Defaulters are liquidated - await troveManager.liquidate(defaulter_1); - await troveManager.liquidate(defaulter_2); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - assert.isFalse(await sortedTroves.contains(defaulter_2)); - - const alice_LUSDDeposit_Before = ( - await stabilityPool.getCompoundedLUSDDeposit(alice) - ).toString(); - const bob_LUSDDeposit_Before = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) - ).toString(); - - const alice_ETHGain_Before = ( - await stabilityPool.getDepositorETHGain(alice) - ).toString(); - const bob_ETHGain_Before = ( - await stabilityPool.getDepositorETHGain(bob) - ).toString(); - - //check non-zero LUSD and ETHGain in the Stability Pool - const LUSDinSP = await stabilityPool.getTotalLUSDDeposits(); - const ETHinSP = await stabilityPool.getETH(); - assert.isTrue(LUSDinSP.gt(mv._zeroBN)); - assert.isTrue(ETHinSP.gt(mv._zeroBN)); - - // Price rises - await priceFeed.setPrice(dec(200, 18)); - - // Carol withdraws her Stability deposit - assert.equal( - (await stabilityPool.deposits(carol))[0].toString(), - dec(30000, 18) - ); - await stabilityPool.withdrawFromSP(dec(30000, 18), { from: carol }); - assert.equal((await stabilityPool.deposits(carol))[0].toString(), "0"); - - const alice_LUSDDeposit_After = ( - await stabilityPool.getCompoundedLUSDDeposit(alice) - ).toString(); - const bob_LUSDDeposit_After = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) - ).toString(); - - const alice_ETHGain_After = ( - await stabilityPool.getDepositorETHGain(alice) - ).toString(); - const bob_ETHGain_After = ( - await stabilityPool.getDepositorETHGain(bob) - ).toString(); - - // Check compounded deposits and ETH gains for A and B have not changed - assert.equal(alice_LUSDDeposit_Before, alice_LUSDDeposit_After); - assert.equal(bob_LUSDDeposit_Before, bob_LUSDDeposit_After); - - assert.equal(alice_ETHGain_Before, alice_ETHGain_After); - assert.equal(bob_ETHGain_Before, bob_ETHGain_After); - }); - - it("withdrawFromSP(): doesn't impact system debt, collateral or TCR ", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - await stabilityPool.provideToSP(dec(20000, 18), frontEnd_1, { - from: bob, - }); - await stabilityPool.provideToSP(dec(30000, 18), frontEnd_1, { - from: carol, - }); - - // Would-be defaulters open troves - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, - }); - - // Price drops - await priceFeed.setPrice(dec(105, 18)); - - // Defaulters are liquidated - await troveManager.liquidate(defaulter_1); - await troveManager.liquidate(defaulter_2); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - assert.isFalse(await sortedTroves.contains(defaulter_2)); - - // Price rises - await priceFeed.setPrice(dec(200, 18)); - - const activeDebt_Before = (await activePool.getLUSDDebt()).toString(); - const defaultedDebt_Before = (await defaultPool.getLUSDDebt()).toString(); - const activeColl_Before = (await activePool.getETH()).toString(); - const defaultedColl_Before = (await defaultPool.getETH()).toString(); - const TCR_Before = (await th.getTCR(contracts)).toString(); - - // Carol withdraws her Stability deposit - assert.equal( - (await stabilityPool.deposits(carol))[0].toString(), - dec(30000, 18) - ); - await stabilityPool.withdrawFromSP(dec(30000, 18), { from: carol }); - assert.equal((await stabilityPool.deposits(carol))[0].toString(), "0"); - - const activeDebt_After = (await activePool.getLUSDDebt()).toString(); - const defaultedDebt_After = (await defaultPool.getLUSDDebt()).toString(); - const activeColl_After = (await activePool.getETH()).toString(); - const defaultedColl_After = (await defaultPool.getETH()).toString(); - const TCR_After = (await th.getTCR(contracts)).toString(); - - // Check total system debt, collateral and TCR have not changed after a Stability deposit is made - assert.equal(activeDebt_Before, activeDebt_After); - assert.equal(defaultedDebt_Before, defaultedDebt_After); - assert.equal(activeColl_Before, activeColl_After); - assert.equal(defaultedColl_Before, defaultedColl_After); - assert.equal(TCR_Before, TCR_After); - }); - - it("withdrawFromSP(): doesn't impact any troves, including the caller's trove", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - // A, B and C provide to SP - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - await stabilityPool.provideToSP(dec(20000, 18), frontEnd_1, { - from: bob, - }); - await stabilityPool.provideToSP(dec(30000, 18), frontEnd_1, { - from: carol, - }); - - // Price drops - await priceFeed.setPrice(dec(105, 18)); - const price = await priceFeed.getPrice(); - - // Get debt, collateral and ICR of all existing troves - const whale_Debt_Before = ( - await troveManager.Troves(whale) - )[0].toString(); - const alice_Debt_Before = ( - await troveManager.Troves(alice) - )[0].toString(); - const bob_Debt_Before = (await troveManager.Troves(bob))[0].toString(); - const carol_Debt_Before = ( - await troveManager.Troves(carol) - )[0].toString(); - - const whale_Coll_Before = ( - await troveManager.Troves(whale) - )[1].toString(); - const alice_Coll_Before = ( - await troveManager.Troves(alice) - )[1].toString(); - const bob_Coll_Before = (await troveManager.Troves(bob))[1].toString(); - const carol_Coll_Before = ( - await troveManager.Troves(carol) - )[1].toString(); - - const whale_ICR_Before = ( - await troveManager.getCurrentICR(whale, price) - ).toString(); - const alice_ICR_Before = ( - await troveManager.getCurrentICR(alice, price) - ).toString(); - const bob_ICR_Before = ( - await troveManager.getCurrentICR(bob, price) - ).toString(); - const carol_ICR_Before = ( - await troveManager.getCurrentICR(carol, price) - ).toString(); - - // price rises - await priceFeed.setPrice(dec(200, 18)); - - // Carol withdraws her Stability deposit - assert.equal( - (await stabilityPool.deposits(carol))[0].toString(), - dec(30000, 18) - ); - await stabilityPool.withdrawFromSP(dec(30000, 18), { from: carol }); - assert.equal((await stabilityPool.deposits(carol))[0].toString(), "0"); - - const whale_Debt_After = (await troveManager.Troves(whale))[0].toString(); - const alice_Debt_After = (await troveManager.Troves(alice))[0].toString(); - const bob_Debt_After = (await troveManager.Troves(bob))[0].toString(); - const carol_Debt_After = (await troveManager.Troves(carol))[0].toString(); - - const whale_Coll_After = (await troveManager.Troves(whale))[1].toString(); - const alice_Coll_After = (await troveManager.Troves(alice))[1].toString(); - const bob_Coll_After = (await troveManager.Troves(bob))[1].toString(); - const carol_Coll_After = (await troveManager.Troves(carol))[1].toString(); - - const whale_ICR_After = ( - await troveManager.getCurrentICR(whale, price) - ).toString(); - const alice_ICR_After = ( - await troveManager.getCurrentICR(alice, price) - ).toString(); - const bob_ICR_After = ( - await troveManager.getCurrentICR(bob, price) - ).toString(); - const carol_ICR_After = ( - await troveManager.getCurrentICR(carol, price) - ).toString(); - - // Check all troves are unaffected by Carol's Stability deposit withdrawal - assert.equal(whale_Debt_Before, whale_Debt_After); - assert.equal(alice_Debt_Before, alice_Debt_After); - assert.equal(bob_Debt_Before, bob_Debt_After); - assert.equal(carol_Debt_Before, carol_Debt_After); - - assert.equal(whale_Coll_Before, whale_Coll_After); - assert.equal(alice_Coll_Before, alice_Coll_After); - assert.equal(bob_Coll_Before, bob_Coll_After); - assert.equal(carol_Coll_Before, carol_Coll_After); - - assert.equal(whale_ICR_Before, whale_ICR_After); - assert.equal(alice_ICR_Before, alice_ICR_After); - assert.equal(bob_ICR_Before, bob_ICR_After); - assert.equal(carol_ICR_Before, carol_ICR_After); - }); - - it("withdrawFromSP(): succeeds when amount is 0 and system has an undercollateralized trove", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { from: A }); - - const A_initialDeposit = (await stabilityPool.deposits(A))[0].toString(); - assert.equal(A_initialDeposit, dec(100, 18)); - - // defaulters opens trove - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, - }); - - // ETH drops, defaulters are in liquidation range - await priceFeed.setPrice(dec(105, 18)); - const price = await priceFeed.getPrice(); - assert.isTrue( - await th.ICRbetween100and110(defaulter_1, troveManager, price) - ); - - await th.fastForwardTime( - timeValues.MINUTES_IN_ONE_WEEK, - web3.currentProvider - ); - - // Liquidate d1 - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - // Check d2 is undercollateralized - assert.isTrue( - await th.ICRbetween100and110(defaulter_2, troveManager, price) - ); - assert.isTrue(await sortedTroves.contains(defaulter_2)); - - const A_ETHBalBefore = toBN(await web3.eth.getBalance(A)); - const A_LQTYBalBefore = await lqtyToken.balanceOf(A); - - // Check Alice has gains to withdraw - const A_pendingETHGain = await stabilityPool.getDepositorETHGain(A); - const A_pendingLQTYGain = await stabilityPool.getDepositorLQTYGain(A); - assert.isTrue(A_pendingETHGain.gt(toBN("0"))); - assert.isTrue(A_pendingLQTYGain.gt(toBN("0"))); - - // Check withdrawal of 0 succeeds - const tx = await stabilityPool.withdrawFromSP(0, { - from: A, - gasPrice: GAS_PRICE, - }); - assert.isTrue(tx.receipt.status); - - const A_expectedBalance = A_ETHBalBefore.sub( - toBN(th.gasUsed(tx) * GAS_PRICE) - ); - - const A_ETHBalAfter = toBN(await web3.eth.getBalance(A)); - - const A_LQTYBalAfter = await lqtyToken.balanceOf(A); - const A_LQTYBalDiff = A_LQTYBalAfter.sub(A_LQTYBalBefore); - - // Check A's ETH and LQTY balances have increased correctly - assert.isTrue(A_ETHBalAfter.sub(A_expectedBalance).eq(A_pendingETHGain)); - assert.isAtMost(th.getDifference(A_LQTYBalDiff, A_pendingLQTYGain), 1000); - }); - - it("withdrawFromSP(): withdrawing 0 LUSD doesn't alter the caller's deposit or the total LUSD in the Stability Pool", async () => { - // --- SETUP --- - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - // A, B, C provides 100, 50, 30 LUSD to SP - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { - from: alice, - }); - await stabilityPool.provideToSP(dec(50, 18), frontEnd_1, { from: bob }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_1, { from: carol }); - - const bob_Deposit_Before = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) - ).toString(); - const LUSDinSP_Before = ( - await stabilityPool.getTotalLUSDDeposits() - ).toString(); - - assert.equal(LUSDinSP_Before, dec(180, 18)); - - // Bob withdraws 0 LUSD from the Stability Pool - await stabilityPool.withdrawFromSP(0, { from: bob }); - - // check Bob's deposit and total LUSD in Stability Pool has not changed - const bob_Deposit_After = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) - ).toString(); - const LUSDinSP_After = ( - await stabilityPool.getTotalLUSDDeposits() - ).toString(); - - assert.equal(bob_Deposit_Before, bob_Deposit_After); - assert.equal(LUSDinSP_Before, LUSDinSP_After); - }); - - it("withdrawFromSP(): withdrawing 0 ETH Gain does not alter the caller's ETH balance, their trove collateral, or the ETH in the Stability Pool", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - // Would-be defaulter open trove - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // Price drops - await priceFeed.setPrice(dec(105, 18)); - - assert.isFalse(await th.checkRecoveryMode(contracts)); - - // Defaulter 1 liquidated, full offset - await troveManager.liquidate(defaulter_1); - - // Dennis opens trove and deposits to Stability Pool - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: dennis }, - }); - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { - from: dennis, - }); - - // Check Dennis has 0 ETHGain - const dennis_ETHGain = ( - await stabilityPool.getDepositorETHGain(dennis) - ).toString(); - assert.equal(dennis_ETHGain, "0"); - - const dennis_ETHBalance_Before = web3.eth.getBalance(dennis).toString(); - const dennis_Collateral_Before = ( - await troveManager.Troves(dennis) - )[1].toString(); - const ETHinSP_Before = (await stabilityPool.getETH()).toString(); - - await priceFeed.setPrice(dec(200, 18)); - - // Dennis withdraws his full deposit and ETHGain to his account - await stabilityPool.withdrawFromSP(dec(100, 18), { - from: dennis, - gasPrice: GAS_PRICE, - }); - - // Check withdrawal does not alter Dennis' ETH balance or his trove's collateral - const dennis_ETHBalance_After = web3.eth.getBalance(dennis).toString(); - const dennis_Collateral_After = ( - await troveManager.Troves(dennis) - )[1].toString(); - const ETHinSP_After = (await stabilityPool.getETH()).toString(); - - assert.equal(dennis_ETHBalance_Before, dennis_ETHBalance_After); - assert.equal(dennis_Collateral_Before, dennis_Collateral_After); - - // Check withdrawal has not altered the ETH in the Stability Pool - assert.equal(ETHinSP_Before, ETHinSP_After); - }); - - it("withdrawFromSP(): Request to withdraw > caller's deposit only withdraws the caller's compounded deposit", async () => { - // --- SETUP --- - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // A, B, C provide LUSD to SP - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - await stabilityPool.provideToSP(dec(20000, 18), frontEnd_1, { - from: bob, - }); - await stabilityPool.provideToSP(dec(30000, 18), frontEnd_1, { - from: carol, - }); - - // Price drops - await priceFeed.setPrice(dec(105, 18)); - - // Liquidate defaulter 1 - await troveManager.liquidate(defaulter_1); - - const alice_LUSD_Balance_Before = await boldToken.balanceOf(alice); - const bob_LUSD_Balance_Before = await boldToken.balanceOf(bob); - - const alice_Deposit_Before = await stabilityPool.getCompoundedLUSDDeposit( - alice - ); - const bob_Deposit_Before = await stabilityPool.getCompoundedLUSDDeposit( - bob - ); - - const LUSDinSP_Before = await stabilityPool.getTotalLUSDDeposits(); - - await priceFeed.setPrice(dec(200, 18)); - - // Bob attempts to withdraws 1 wei more than his compounded deposit from the Stability Pool - await stabilityPool.withdrawFromSP(bob_Deposit_Before.add(toBN(1)), { - from: bob, - }); - - // Check Bob's LUSD balance has risen by only the value of his compounded deposit - const bob_expectedLUSDBalance = bob_LUSD_Balance_Before - .add(bob_Deposit_Before) - .toString(); - const bob_LUSD_Balance_After = ( - await boldToken.balanceOf(bob) - ).toString(); - assert.equal(bob_LUSD_Balance_After, bob_expectedLUSDBalance); - - // Alice attempts to withdraws 2309842309.000000000000000000 LUSD from the Stability Pool - await stabilityPool.withdrawFromSP("2309842309000000000000000000", { - from: alice, - }); - - // Check Alice's LUSD balance has risen by only the value of her compounded deposit - const alice_expectedLUSDBalance = alice_LUSD_Balance_Before - .add(alice_Deposit_Before) - .toString(); - const alice_LUSD_Balance_After = ( - await boldToken.balanceOf(alice) - ).toString(); - assert.equal(alice_LUSD_Balance_After, alice_expectedLUSDBalance); - - // Check LUSD in Stability Pool has been reduced by only Alice's compounded deposit and Bob's compounded deposit - const expectedLUSDinSP = LUSDinSP_Before.sub(alice_Deposit_Before) - .sub(bob_Deposit_Before) - .toString(); - const LUSDinSP_After = ( - await stabilityPool.getTotalLUSDDeposits() - ).toString(); - assert.equal(LUSDinSP_After, expectedLUSDinSP); - }); - - it("withdrawFromSP(): Request to withdraw 2^256-1 LUSD only withdraws the caller's compounded deposit", async () => { - // --- SETUP --- - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves - // A, B, C open troves - // A, B, C open troves - // A, B, C open troves - // A, B, C open troves - // A, B, C open troves - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // A, B, C provides 100, 50, 30 LUSD to SP - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { - from: alice, - }); - await stabilityPool.provideToSP(dec(50, 18), frontEnd_1, { from: bob }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_1, { from: carol }); - - // Price drops - await priceFeed.setPrice(dec(100, 18)); - - // Liquidate defaulter 1 - await troveManager.liquidate(defaulter_1); - - const bob_LUSD_Balance_Before = await boldToken.balanceOf(bob); - - const bob_Deposit_Before = await stabilityPool.getCompoundedLUSDDeposit( - bob - ); - - const LUSDinSP_Before = await stabilityPool.getTotalLUSDDeposits(); - - const maxBytes32 = web3.utils.toBN( - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ); - - // Price drops - await priceFeed.setPrice(dec(200, 18)); - - // Bob attempts to withdraws maxBytes32 LUSD from the Stability Pool - await stabilityPool.withdrawFromSP(maxBytes32, { from: bob }); - - // Check Bob's LUSD balance has risen by only the value of his compounded deposit - const bob_expectedLUSDBalance = bob_LUSD_Balance_Before - .add(bob_Deposit_Before) - .toString(); - const bob_LUSD_Balance_After = ( - await boldToken.balanceOf(bob) - ).toString(); - assert.equal(bob_LUSD_Balance_After, bob_expectedLUSDBalance); - - // Check LUSD in Stability Pool has been reduced by only Bob's compounded deposit - const expectedLUSDinSP = - LUSDinSP_Before.sub(bob_Deposit_Before).toString(); - const LUSDinSP_After = ( - await stabilityPool.getTotalLUSDDeposits() - ).toString(); - assert.equal(LUSDinSP_After, expectedLUSDinSP); - }); - - it("withdrawFromSP(): caller can withdraw full deposit and ETH gain during Recovery Mode", async () => { - // --- SETUP --- - - // Price doubles - await priceFeed.setPrice(dec(400, 18)); - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: whale }, - }); - // Price halves - await priceFeed.setPrice(dec(200, 18)); - - // A, B, C open troves and make Stability Pool deposits - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(4, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(4, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(4, 18)), - extraParams: { from: carol }, - }); - - await borrowerOperations.openTrove( - th._100pct, - await getOpenTroveLUSDAmount(dec(10000, 18)), - defaulter_1, - defaulter_1, - { from: defaulter_1, value: dec(100, "ether") } - ); - - // A, B, C provides 10000, 5000, 3000 LUSD to SP - const A_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - gasPrice: GAS_PRICE, - }) - ); - const B_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(5000, 18), frontEnd_1, { - from: bob, - gasPrice: GAS_PRICE, - }) - ); - const C_GAS_Used = th.gasUsed( - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_1, { - from: carol, - gasPrice: GAS_PRICE, - }) - ); - - // Price drops - await priceFeed.setPrice(dec(105, 18)); - const price = await priceFeed.getPrice(); - - assert.isTrue(await th.checkRecoveryMode(contracts)); - - // Liquidate defaulter 1 - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - const alice_LUSD_Balance_Before = await boldToken.balanceOf(alice); - const bob_LUSD_Balance_Before = await boldToken.balanceOf(bob); - const carol_LUSD_Balance_Before = await boldToken.balanceOf(carol); - - const alice_ETH_Balance_Before = web3.utils.toBN( - await web3.eth.getBalance(alice) - ); - const bob_ETH_Balance_Before = web3.utils.toBN( - await web3.eth.getBalance(bob) - ); - const carol_ETH_Balance_Before = web3.utils.toBN( - await web3.eth.getBalance(carol) - ); - - const alice_Deposit_Before = await stabilityPool.getCompoundedLUSDDeposit( - alice - ); - const bob_Deposit_Before = await stabilityPool.getCompoundedLUSDDeposit( - bob - ); - const carol_Deposit_Before = await stabilityPool.getCompoundedLUSDDeposit( - carol - ); - - const alice_ETHGain_Before = await stabilityPool.getDepositorETHGain( - alice - ); - const bob_ETHGain_Before = await stabilityPool.getDepositorETHGain(bob); - const carol_ETHGain_Before = await stabilityPool.getDepositorETHGain( - carol - ); - - const LUSDinSP_Before = await stabilityPool.getTotalLUSDDeposits(); - - // Price rises - await priceFeed.setPrice(dec(220, 18)); - - assert.isTrue(await th.checkRecoveryMode(contracts)); - - // A, B, C withdraw their full deposits from the Stability Pool - const A_GAS_Deposit = th.gasUsed( - await stabilityPool.withdrawFromSP(dec(10000, 18), { - from: alice, - gasPrice: GAS_PRICE, - }) - ); - const B_GAS_Deposit = th.gasUsed( - await stabilityPool.withdrawFromSP(dec(5000, 18), { - from: bob, - gasPrice: GAS_PRICE, - }) - ); - const C_GAS_Deposit = th.gasUsed( - await stabilityPool.withdrawFromSP(dec(3000, 18), { - from: carol, - gasPrice: GAS_PRICE, - }) - ); - - // Check LUSD balances of A, B, C have risen by the value of their compounded deposits, respectively - const alice_expectedLUSDBalance = alice_LUSD_Balance_Before - .add(alice_Deposit_Before) - .toString(); - - const bob_expectedLUSDBalance = bob_LUSD_Balance_Before - .add(bob_Deposit_Before) - .toString(); - const carol_expectedLUSDBalance = carol_LUSD_Balance_Before - .add(carol_Deposit_Before) - .toString(); - - const alice_LUSD_Balance_After = ( - await boldToken.balanceOf(alice) - ).toString(); - - const bob_LUSD_Balance_After = ( - await boldToken.balanceOf(bob) - ).toString(); - const carol_LUSD_Balance_After = ( - await boldToken.balanceOf(carol) - ).toString(); - - assert.equal(alice_LUSD_Balance_After, alice_expectedLUSDBalance); - assert.equal(bob_LUSD_Balance_After, bob_expectedLUSDBalance); - assert.equal(carol_LUSD_Balance_After, carol_expectedLUSDBalance); - - // Check ETH balances of A, B, C have increased by the value of their ETH gain from liquidations, respectively - const alice_expectedETHBalance = alice_ETH_Balance_Before - .add(alice_ETHGain_Before) - .toString(); - const bob_expectedETHBalance = bob_ETH_Balance_Before - .add(bob_ETHGain_Before) - .toString(); - const carol_expectedETHBalance = carol_ETH_Balance_Before - .add(carol_ETHGain_Before) - .toString(); - - const alice_ETHBalance_After = ( - await web3.eth.getBalance(alice) - ).toString(); - const bob_ETHBalance_After = (await web3.eth.getBalance(bob)).toString(); - const carol_ETHBalance_After = ( - await web3.eth.getBalance(carol) - ).toString(); - - // ETH balances before minus gas used - const alice_ETHBalance_After_Gas = alice_ETHBalance_After - A_GAS_Used; - const bob_ETHBalance_After_Gas = bob_ETHBalance_After - B_GAS_Used; - const carol_ETHBalance_After_Gas = carol_ETHBalance_After - C_GAS_Used; - - assert.equal(alice_expectedETHBalance, alice_ETHBalance_After_Gas); - assert.equal(bob_expectedETHBalance, bob_ETHBalance_After_Gas); - assert.equal(carol_expectedETHBalance, carol_ETHBalance_After_Gas); - - // Check LUSD in Stability Pool has been reduced by A, B and C's compounded deposit - const expectedLUSDinSP = LUSDinSP_Before.sub(alice_Deposit_Before) - .sub(bob_Deposit_Before) - .sub(carol_Deposit_Before) - .toString(); - const LUSDinSP_After = ( - await stabilityPool.getTotalLUSDDeposits() - ).toString(); - assert.equal(LUSDinSP_After, expectedLUSDinSP); - - // Check ETH in SP has reduced to zero - const ETHinSP_After = (await stabilityPool.getETH()).toString(); - assert.isAtMost(th.getDifference(ETHinSP_After, "0"), 100000); - }); - - it("getDepositorETHGain(): depositor does not earn further ETH gains from liquidations while their compounded deposit == 0: ", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(1, 24)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, - }); - - // defaulters open troves - await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_2 }, - }); - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_3 }, - }); - - // A, B, provide 10000, 5000 LUSD to SP - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - await stabilityPool.provideToSP(dec(5000, 18), frontEnd_1, { from: bob }); - - //price drops - await priceFeed.setPrice(dec(105, 18)); - - // Liquidate defaulter 1. Empties the Pool - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - const LUSDinSP = (await stabilityPool.getTotalLUSDDeposits()).toString(); - assert.equal(LUSDinSP, "0"); - - // Check Stability deposits have been fully cancelled with debt, and are now all zero - const alice_Deposit = ( - await stabilityPool.getCompoundedLUSDDeposit(alice) - ).toString(); - const bob_Deposit = ( - await stabilityPool.getCompoundedLUSDDeposit(bob) - ).toString(); - - assert.equal(alice_Deposit, "0"); - assert.equal(bob_Deposit, "0"); - - // Get ETH gain for A and B - const alice_ETHGain_1 = ( - await stabilityPool.getDepositorETHGain(alice) - ).toString(); - const bob_ETHGain_1 = ( - await stabilityPool.getDepositorETHGain(bob) - ).toString(); - - // Whale deposits 10000 LUSD to Stability Pool - await stabilityPool.provideToSP(dec(1, 24), frontEnd_1, { from: whale }); - - // Liquidation 2 - await troveManager.liquidate(defaulter_2); - assert.isFalse(await sortedTroves.contains(defaulter_2)); - - // Check Alice and Bob have not received ETH gain from liquidation 2 while their deposit was 0 - const alice_ETHGain_2 = ( - await stabilityPool.getDepositorETHGain(alice) - ).toString(); - const bob_ETHGain_2 = ( - await stabilityPool.getDepositorETHGain(bob) - ).toString(); - - assert.equal(alice_ETHGain_1, alice_ETHGain_2); - assert.equal(bob_ETHGain_1, bob_ETHGain_2); - - // Liquidation 3 - await troveManager.liquidate(defaulter_3); - assert.isFalse(await sortedTroves.contains(defaulter_3)); - - // Check Alice and Bob have not received ETH gain from liquidation 3 while their deposit was 0 - const alice_ETHGain_3 = ( - await stabilityPool.getDepositorETHGain(alice) - ).toString(); - const bob_ETHGain_3 = ( - await stabilityPool.getDepositorETHGain(bob) - ).toString(); - - assert.equal(alice_ETHGain_1, alice_ETHGain_3); - assert.equal(bob_ETHGain_1, bob_ETHGain_3); - }); - - // --- LQTY functionality --- - it("withdrawFromSP(): triggers LQTY reward event - increases the sum G", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(1, 24)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - - // A and B provide to SP - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(10000, 18), ZERO_ADDRESS, { - from: B, - }); - - const G_Before = await stabilityPool.epochToScaleToG(0, 0); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // A withdraws from SP - await stabilityPool.withdrawFromSP(dec(5000, 18), { from: A }); - - const G_1 = await stabilityPool.epochToScaleToG(0, 0); - - // Expect G has increased from the LQTY reward event triggered - assert.isTrue(G_1.gt(G_Before)); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // A withdraws from SP - await stabilityPool.withdrawFromSP(dec(5000, 18), { from: B }); - - const G_2 = await stabilityPool.epochToScaleToG(0, 0); - - // Expect G has increased from the LQTY reward event triggered - assert.isTrue(G_2.gt(G_1)); - }); - - it("withdrawFromSP(), partial withdrawal: doesn't change the front end tag", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // whale transfer to troves D and E - await boldToken.transfer(D, dec(100, 18), { from: whale }); - await boldToken.transfer(E, dec(200, 18), { from: whale }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - - // A, B, C, D, E provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), ZERO_ADDRESS, { from: C }); - await stabilityPool.provideToSP(dec(40, 18), frontEnd_1, { from: D }); - await stabilityPool.provideToSP(dec(50, 18), ZERO_ADDRESS, { from: E }); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // A, B, C, D, E withdraw, from different front ends - await stabilityPool.withdrawFromSP(dec(5, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(10, 18), { from: B }); - await stabilityPool.withdrawFromSP(dec(15, 18), { from: C }); - await stabilityPool.withdrawFromSP(dec(20, 18), { from: D }); - await stabilityPool.withdrawFromSP(dec(25, 18), { from: E }); - - const frontEndTag_A = (await stabilityPool.deposits(A))[1]; - const frontEndTag_B = (await stabilityPool.deposits(B))[1]; - const frontEndTag_C = (await stabilityPool.deposits(C))[1]; - const frontEndTag_D = (await stabilityPool.deposits(D))[1]; - const frontEndTag_E = (await stabilityPool.deposits(E))[1]; - - // Check deposits are still tagged with their original front end - assert.equal(frontEndTag_A, frontEnd_1); - assert.equal(frontEndTag_B, frontEnd_2); - assert.equal(frontEndTag_C, ZERO_ADDRESS); - assert.equal(frontEndTag_D, frontEnd_1); - assert.equal(frontEndTag_E, ZERO_ADDRESS); - }); - - it("withdrawFromSP(), partial withdrawal: depositor receives LQTY rewards", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - - // A, B, C, provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), ZERO_ADDRESS, { from: C }); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // Get A, B, C LQTY balance before - const A_LQTYBalance_Before = await lqtyToken.balanceOf(A); - const B_LQTYBalance_Before = await lqtyToken.balanceOf(B); - const C_LQTYBalance_Before = await lqtyToken.balanceOf(C); - - // A, B, C withdraw - await stabilityPool.withdrawFromSP(dec(1, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(2, 18), { from: B }); - await stabilityPool.withdrawFromSP(dec(3, 18), { from: C }); - - // Get LQTY balance after - const A_LQTYBalance_After = await lqtyToken.balanceOf(A); - const B_LQTYBalance_After = await lqtyToken.balanceOf(B); - const C_LQTYBalance_After = await lqtyToken.balanceOf(C); - - // Check LQTY Balance of A, B, C has increased - assert.isTrue(A_LQTYBalance_After.gt(A_LQTYBalance_Before)); - assert.isTrue(B_LQTYBalance_After.gt(B_LQTYBalance_Before)); - assert.isTrue(C_LQTYBalance_After.gt(C_LQTYBalance_Before)); - }); - - it("withdrawFromSP(), partial withdrawal: tagged front end receives LQTY rewards", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - - // A, B, C, provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: C }); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // Get front ends' LQTY balance before - const F1_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_1); - const F2_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_2); - const F3_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_3); - - // A, B, C withdraw - await stabilityPool.withdrawFromSP(dec(1, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(2, 18), { from: B }); - await stabilityPool.withdrawFromSP(dec(3, 18), { from: C }); - - // Get front ends' LQTY balance after - const F1_LQTYBalance_After = await lqtyToken.balanceOf(A); - const F2_LQTYBalance_After = await lqtyToken.balanceOf(B); - const F3_LQTYBalance_After = await lqtyToken.balanceOf(C); - - // Check LQTY Balance of front ends has increased - assert.isTrue(F1_LQTYBalance_After.gt(F1_LQTYBalance_Before)); - assert.isTrue(F2_LQTYBalance_After.gt(F2_LQTYBalance_Before)); - assert.isTrue(F3_LQTYBalance_After.gt(F3_LQTYBalance_Before)); - }); - - it("withdrawFromSP(), partial withdrawal: tagged front end's stake decreases", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C, D, E, F open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: E }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: F }, - }); - - // A, B, C, D, E, F provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: C }); - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: D }); - await stabilityPool.provideToSP(dec(20, 18), frontEnd_2, { from: E }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_3, { from: F }); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - // Get front ends' stake before - const F1_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_3); - - // A, B, C withdraw - await stabilityPool.withdrawFromSP(dec(1, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(2, 18), { from: B }); - await stabilityPool.withdrawFromSP(dec(3, 18), { from: C }); - - // Get front ends' stakes after - const F1_Stake_After = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_After = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_After = await stabilityPool.frontEndStakes(frontEnd_3); - - // Check front ends' stakes have decreased - assert.isTrue(F1_Stake_After.lt(F1_Stake_Before)); - assert.isTrue(F2_Stake_After.lt(F2_Stake_Before)); - assert.isTrue(F3_Stake_After.lt(F3_Stake_Before)); - }); - - it("withdrawFromSP(), partial withdrawal: tagged front end's snapshots update", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A, B, C, open troves - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(40000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(60000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - - // D opens trove - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, - }); - - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // --- SETUP --- - - const deposit_A = dec(10000, 18); - const deposit_B = dec(20000, 18); - const deposit_C = dec(30000, 18); - - // A, B, C make their initial deposits - await stabilityPool.provideToSP(deposit_A, frontEnd_1, { from: A }); - await stabilityPool.provideToSP(deposit_B, frontEnd_2, { from: B }); - await stabilityPool.provideToSP(deposit_C, frontEnd_3, { from: C }); - - // fastforward time then make an SP deposit, to make G > 0 - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - - await stabilityPool.provideToSP(dec(1000, 18), ZERO_ADDRESS, { from: D }); - - // perform a liquidation to make 0 < P < 1, and S > 0 - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - - await troveManager.liquidate(defaulter_1); - - const currentEpoch = await stabilityPool.currentEpoch(); - const currentScale = await stabilityPool.currentScale(); - - const S_Before = await stabilityPool.epochToScaleToSum( - currentEpoch, - currentScale - ); - const P_Before = await stabilityPool.P(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); - - // Confirm 0 < P < 1 - assert.isTrue(P_Before.gt(toBN("0")) && P_Before.lt(toBN(dec(1, 18)))); - // Confirm S, G are both > 0 - assert.isTrue(S_Before.gt(toBN("0"))); - assert.isTrue(G_Before.gt(toBN("0"))); - - // Get front ends' snapshots before - for (frontEnd of [frontEnd_1, frontEnd_2, frontEnd_3]) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); - - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends, since S corresponds to ETH gain) - assert.equal(snapshot[1], dec(1, 18)); // P - assert.equal(snapshot[2], "0"); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - - // --- TEST --- - - await priceFeed.setPrice(dec(200, 18)); - - // A, B, C top withdraw part of their deposits. Grab G at each stage, as it can increase a bit - // between topups, because some block.timestamp time passes (and LQTY is issued) between ops - const G1 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.withdrawFromSP(dec(1, 18), { from: A }); - - const G2 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.withdrawFromSP(dec(2, 18), { from: B }); - - const G3 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.withdrawFromSP(dec(3, 18), { from: C }); - - const frontEnds = [frontEnd_1, frontEnd_2, frontEnd_3]; - const G_Values = [G1, G2, G3]; - - // Map frontEnds to the value of G at time the deposit was made - frontEndToG = th.zipToObject(frontEnds, G_Values); - - // Get front ends' snapshots after - for (const [frontEnd, G] of Object.entries(frontEndToG)) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); - - // Check snapshots are the expected values - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends) - assert.isTrue(snapshot[1].eq(P_Before)); // P - assert.isTrue(snapshot[2].eq(G)); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - }); - - it("withdrawFromSP(), full withdrawal: removes deposit's front end tag", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // Whale transfers to A, B - await boldToken.transfer(A, dec(10000, 18), { from: whale }); - await boldToken.transfer(B, dec(20000, 18), { from: whale }); - - //C, D open troves - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(40000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, - }); - - // A, B, C, D make their initial deposits - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20000, 18), ZERO_ADDRESS, { - from: B, - }); - await stabilityPool.provideToSP(dec(30000, 18), frontEnd_2, { from: C }); - await stabilityPool.provideToSP(dec(40000, 18), ZERO_ADDRESS, { - from: D, - }); - - // Check deposits are tagged with correct front end - const A_tagBefore = await getFrontEndTag(stabilityPool, A); - const B_tagBefore = await getFrontEndTag(stabilityPool, B); - const C_tagBefore = await getFrontEndTag(stabilityPool, C); - const D_tagBefore = await getFrontEndTag(stabilityPool, D); - - assert.equal(A_tagBefore, frontEnd_1); - assert.equal(B_tagBefore, ZERO_ADDRESS); - assert.equal(C_tagBefore, frontEnd_2); - assert.equal(D_tagBefore, ZERO_ADDRESS); - - // All depositors make full withdrawal - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(20000, 18), { from: B }); - await stabilityPool.withdrawFromSP(dec(30000, 18), { from: C }); - await stabilityPool.withdrawFromSP(dec(40000, 18), { from: D }); - - // Check all deposits now have no front end tag - const A_tagAfter = await getFrontEndTag(stabilityPool, A); - const B_tagAfter = await getFrontEndTag(stabilityPool, B); - const C_tagAfter = await getFrontEndTag(stabilityPool, C); - const D_tagAfter = await getFrontEndTag(stabilityPool, D); - - assert.equal(A_tagAfter, ZERO_ADDRESS); - assert.equal(B_tagAfter, ZERO_ADDRESS); - assert.equal(C_tagAfter, ZERO_ADDRESS); - assert.equal(D_tagAfter, ZERO_ADDRESS); - }); - - it("withdrawFromSP(), full withdrawal: zero's depositor's snapshots", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // SETUP: Execute a series of operations to make G, S > 0 and P < 1 - - // E opens trove and makes a deposit - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: E }, - }); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_3, { from: E }); - - // Fast-forward time and make a second deposit, to trigger LQTY reward and make G > 0 - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_3, { from: E }); - - // perform a liquidation to make 0 < P < 1, and S > 0 - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - - await troveManager.liquidate(defaulter_1); - - const currentEpoch = await stabilityPool.currentEpoch(); - const currentScale = await stabilityPool.currentScale(); - - const S_Before = await stabilityPool.epochToScaleToSum( - currentEpoch, - currentScale - ); - const P_Before = await stabilityPool.P(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); - - // Confirm 0 < P < 1 - assert.isTrue(P_Before.gt(toBN("0")) && P_Before.lt(toBN(dec(1, 18)))); - // Confirm S, G are both > 0 - assert.isTrue(S_Before.gt(toBN("0"))); - assert.isTrue(G_Before.gt(toBN("0"))); - - // --- TEST --- - - // Whale transfers to A, B - await boldToken.transfer(A, dec(10000, 18), { from: whale }); - await boldToken.transfer(B, dec(20000, 18), { from: whale }); - - await priceFeed.setPrice(dec(200, 18)); - - // C, D open troves - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: C }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(40000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: D }, - }); - - // A, B, C, D make their initial deposits - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20000, 18), ZERO_ADDRESS, { - from: B, - }); - await stabilityPool.provideToSP(dec(30000, 18), frontEnd_2, { from: C }); - await stabilityPool.provideToSP(dec(40000, 18), ZERO_ADDRESS, { - from: D, - }); - - // Check deposits snapshots are non-zero - - for (depositor of [A, B, C, D]) { - const snapshot = await stabilityPool.depositSnapshots(depositor); - - const ZERO = toBN("0"); - // Check S,P, G snapshots are non-zero - assert.isTrue(snapshot[0].eq(S_Before)); // S - assert.isTrue(snapshot[1].eq(P_Before)); // P - assert.isTrue(snapshot[2].gt(ZERO)); // GL increases a bit between each depositor op, so just check it is non-zero - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - - // All depositors make full withdrawal - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(20000, 18), { from: B }); - await stabilityPool.withdrawFromSP(dec(30000, 18), { from: C }); - await stabilityPool.withdrawFromSP(dec(40000, 18), { from: D }); - - // Check all depositors' snapshots have been zero'd - for (depositor of [A, B, C, D]) { - const snapshot = await stabilityPool.depositSnapshots(depositor); - - // Check S, P, G snapshots are now zero - assert.equal(snapshot[0], "0"); // S - assert.equal(snapshot[1], "0"); // P - assert.equal(snapshot[2], "0"); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - }); - - it("withdrawFromSP(), full withdrawal that reduces front end stake to 0: zero’s the front end’s snapshots", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // SETUP: Execute a series of operations to make G, S > 0 and P < 1 - - // E opens trove and makes a deposit - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: E }, - }); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_3, { from: E }); - - // Fast-forward time and make a second deposit, to trigger LQTY reward and make G > 0 - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_3, { from: E }); - - // perform a liquidation to make 0 < P < 1, and S > 0 - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - - await troveManager.liquidate(defaulter_1); - - const currentEpoch = await stabilityPool.currentEpoch(); - const currentScale = await stabilityPool.currentScale(); - - const S_Before = await stabilityPool.epochToScaleToSum( - currentEpoch, - currentScale - ); - const P_Before = await stabilityPool.P(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); - - // Confirm 0 < P < 1 - assert.isTrue(P_Before.gt(toBN("0")) && P_Before.lt(toBN(dec(1, 18)))); - // Confirm S, G are both > 0 - assert.isTrue(S_Before.gt(toBN("0"))); - assert.isTrue(G_Before.gt(toBN("0"))); - - // --- TEST --- - - // A, B open troves - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(40000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); - - // A, B, make their initial deposits - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20000, 18), frontEnd_2, { from: B }); - - // Check frontend snapshots are non-zero - for (frontEnd of [frontEnd_1, frontEnd_2]) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); - - const ZERO = toBN("0"); - // Check S,P, G snapshots are non-zero - assert.equal(snapshot[0], "0"); // S (always zero for front-end) - assert.isTrue(snapshot[1].eq(P_Before)); // P - assert.isTrue(snapshot[2].gt(ZERO)); // GL increases a bit between each depositor op, so just check it is non-zero - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - - await priceFeed.setPrice(dec(200, 18)); - - // All depositors make full withdrawal - await stabilityPool.withdrawFromSP(dec(10000, 18), { from: A }); - await stabilityPool.withdrawFromSP(dec(20000, 18), { from: B }); - - // Check all front ends' snapshots have been zero'd - for (frontEnd of [frontEnd_1, frontEnd_2]) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); - - // Check S, P, G snapshots are now zero - assert.equal(snapshot[0], "0"); // S (always zero for front-end) - assert.equal(snapshot[1], "0"); // P - assert.equal(snapshot[2], "0"); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - }); - - it("withdrawFromSP(), reverts when initial deposit value is 0", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - // A opens trove and join the Stability Pool - await openTrove({ - extraLUSDAmount: toBN(dec(10100, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { from: A }); - - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // SETUP: Execute a series of operations to trigger LQTY and ETH rewards for depositor A - - // Fast-forward time and make a second deposit, to trigger LQTY reward and make G > 0 - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); - await stabilityPool.provideToSP(dec(100, 18), frontEnd_1, { from: A }); - - // perform a liquidation to make 0 < P < 1, and S > 0 - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - await priceFeed.setPrice(dec(200, 18)); - - // A successfully withraws deposit and all gains - await stabilityPool.withdrawFromSP(dec(10100, 18), { from: A }); - - // Confirm A's recorded deposit is 0 - const A_deposit = (await stabilityPool.deposits(A))[0]; // get initialValue property on deposit struct - assert.equal(A_deposit, "0"); - - // --- TEST --- - const expectedRevertMessage = - "StabilityPool: User must have a non-zero deposit"; - - // Further withdrawal attempt from A - const withdrawalPromise_A = stabilityPool.withdrawFromSP(dec(10000, 18), { - from: A, - }); - await th.assertRevert(withdrawalPromise_A, expectedRevertMessage); - - // Withdrawal attempt of a non-existent deposit, from C - const withdrawalPromise_C = stabilityPool.withdrawFromSP(dec(10000, 18), { - from: C, - }); - await th.assertRevert(withdrawalPromise_C, expectedRevertMessage); - }); - - // --- withdrawETHGainToTrove --- - - it("withdrawETHGainToTrove(): reverts when user has no active deposit", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: alice, - }); - - const alice_initialDeposit = ( - await stabilityPool.deposits(alice) - )[0].toString(); - const bob_initialDeposit = ( - await stabilityPool.deposits(bob) - )[0].toString(); - - assert.equal(alice_initialDeposit, dec(10000, 18)); - assert.equal(bob_initialDeposit, "0"); - - // Defaulter opens a trove, price drops, defaulter gets liquidated - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - const txAlice = await stabilityPool.withdrawETHGainToTrove(alice, alice, { - from: alice, - }); - assert.isTrue(txAlice.receipt.status); - - const txPromise_B = stabilityPool.withdrawETHGainToTrove(bob, bob, { - from: bob, - }); - await th.assertRevert(txPromise_B); - }); - - it("withdrawETHGainToTrove(): Applies LUSDLoss to user's deposit, and redirects ETH reward to user's Trove", async () => { - // --- SETUP --- - // Whale deposits 185000 LUSD in StabilityPool - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); - - // Defaulter opens trove - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // --- TEST --- + // Price halves + await priceFeed.setPrice(dec(200, 18)); - // Alice makes deposit #1: 15000 LUSD + // A, B, C open troves and make Stability Pool deposits await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(10, 18)), + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(4, 18)), extraParams: { from: alice }, }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { - from: alice, + await openTrove({ + extraBoldAmount: toBN(dec(20000, 18)), + ICR: toBN(dec(4, 18)), + extraParams: { from: bob }, }); - - // check Alice's Trove recorded ETH Before: - const aliceTrove_Before = await troveManager.Troves(alice); - const aliceTrove_ETH_Before = aliceTrove_Before[1]; - assert.isTrue(aliceTrove_ETH_Before.gt(toBN("0"))); - - // price drops: defaulter's Trove falls below MCR, alice and whale Trove remain active - await priceFeed.setPrice(dec(105, 18)); - - // Defaulter's Trove is closed - const liquidationTx_1 = await troveManager.liquidate(defaulter_1, { - from: owner, + await openTrove({ + extraBoldAmount: toBN(dec(30000, 18)), + ICR: toBN(dec(4, 18)), + extraParams: { from: carol }, }); - const [liquidatedDebt, liquidatedColl, ,] = - th.getEmittedLiquidationValues(liquidationTx_1); - const ETHGain_A = await stabilityPool.getDepositorETHGain(alice); - const compoundedDeposit_A = await stabilityPool.getCompoundedLUSDDeposit( - alice + await borrowerOperations.openTrove( + th._100pct, + await getOpenTroveBoldAmount(dec(10000, 18)), + defaulter_1, + defaulter_1, + { from: defaulter_1, value: dec(100, "ether") } ); - // Alice should receive rewards proportional to her deposit as share of total deposits - const expectedETHGain_A = liquidatedColl - .mul(toBN(dec(15000, 18))) - .div(toBN(dec(200000, 18))); - const expectedLUSDLoss_A = liquidatedDebt - .mul(toBN(dec(15000, 18))) - .div(toBN(dec(200000, 18))); - const expectedCompoundedDeposit_A = toBN(dec(15000, 18)).sub( - expectedLUSDLoss_A + // A, B, C provides 10000, 5000, 3000 Bold to SP + const A_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, + gasPrice: GAS_PRICE, + }) ); - - assert.isAtMost( - th.getDifference(expectedCompoundedDeposit_A, compoundedDeposit_A), - 100000 + const B_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(5000, 18), { + from: bob, + gasPrice: GAS_PRICE, + }) ); - - // Alice sends her ETH Gains to her Trove - await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); - - // check Alice's LUSDLoss has been applied to her deposit expectedCompoundedDeposit_A - alice_deposit_afterDefault = (await stabilityPool.deposits(alice))[0]; - assert.isAtMost( - th.getDifference( - alice_deposit_afterDefault, - expectedCompoundedDeposit_A - ), - 100000 + const C_GAS_Used = th.gasUsed( + await stabilityPool.provideToSP(dec(3000, 18), { + from: carol, + gasPrice: GAS_PRICE, + }) ); - // check alice's Trove recorded ETH has increased by the expected reward amount - const aliceTrove_After = await troveManager.Troves(alice); - const aliceTrove_ETH_After = aliceTrove_After[1]; - - const Trove_ETH_Increase = aliceTrove_ETH_After - .sub(aliceTrove_ETH_Before) - .toString(); - - assert.equal(Trove_ETH_Increase, ETHGain_A); - }); - - it("withdrawETHGainToTrove(): reverts if it would leave trove with ICR < MCR", async () => { - // --- SETUP --- - // Whale deposits 1850 LUSD in StabilityPool - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); - - // defaulter opened - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); - - // --- TEST --- + // Price drops + await priceFeed.setPrice(dec(105, 18)); + const price = await priceFeed.getPrice(); - // Alice makes deposit #1: 15000 LUSD - await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { - from: alice, - }); + assert.isTrue(await th.checkRecoveryMode(contracts)); - // check alice's Trove recorded ETH Before: - const aliceTrove_Before = await troveManager.Troves(alice); - const aliceTrove_ETH_Before = aliceTrove_Before[1]; - assert.isTrue(aliceTrove_ETH_Before.gt(toBN("0"))); + // Liquidate defaulter 1 + await troveManager.liquidate(defaulter_1); + assert.isFalse(await sortedTroves.contains(defaulter_1)); - // price drops: defaulter's Trove falls below MCR - await priceFeed.setPrice(dec(10, 18)); + const alice_Bold_Balance_Before = await boldToken.balanceOf(alice); + const bob_Bold_Balance_Before = await boldToken.balanceOf(bob); + const carol_Bold_Balance_Before = await boldToken.balanceOf(carol); - // defaulter's Trove is closed. - await troveManager.liquidate(defaulter_1, { from: owner }); + const alice_ETH_Balance_Before = web3.utils.toBN( + await web3.eth.getBalance(alice) + ); + const bob_ETH_Balance_Before = web3.utils.toBN( + await web3.eth.getBalance(bob) + ); + const carol_ETH_Balance_Before = web3.utils.toBN( + await web3.eth.getBalance(carol) + ); - // Alice attempts to her ETH Gains to her Trove - await assertRevert( - stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }), - "BorrowerOps: An operation that would result in ICR < MCR is not permitted" + const alice_Deposit_Before = await stabilityPool.getCompoundedBoldDeposit( + alice + ); + const bob_Deposit_Before = await stabilityPool.getCompoundedBoldDeposit( + bob + ); + const carol_Deposit_Before = await stabilityPool.getCompoundedBoldDeposit( + carol ); - }); - it("withdrawETHGainToTrove(): Subsequent deposit and withdrawal attempt from same account, with no intermediate liquidations, withdraws zero ETH", async () => { - // --- SETUP --- - // Whale deposits 1850 LUSD in StabilityPool - await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, - }); + const alice_ETHGain_Before = await stabilityPool.getDepositorETHGain( + alice + ); + const bob_ETHGain_Before = await stabilityPool.getDepositorETHGain(bob); + const carol_ETHGain_Before = await stabilityPool.getDepositorETHGain( + carol + ); - // defaulter opened - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); + const BoldinSP_Before = await stabilityPool.getTotalBoldDeposits(); - // --- TEST --- + // Price rises + await priceFeed.setPrice(dec(220, 18)); - // Alice makes deposit #1: 15000 LUSD - await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { - from: alice, - }); + assert.isTrue(await th.checkRecoveryMode(contracts)); - // check alice's Trove recorded ETH Before: - const aliceTrove_Before = await troveManager.Troves(alice); - const aliceTrove_ETH_Before = aliceTrove_Before[1]; - assert.isTrue(aliceTrove_ETH_Before.gt(toBN("0"))); + // A, B, C withdraw their full deposits from the Stability Pool + const A_GAS_Deposit = th.gasUsed( + await stabilityPool.withdrawFromSP(dec(10000, 18), { + from: alice, + gasPrice: GAS_PRICE, + }) + ); + const B_GAS_Deposit = th.gasUsed( + await stabilityPool.withdrawFromSP(dec(5000, 18), { + from: bob, + gasPrice: GAS_PRICE, + }) + ); + const C_GAS_Deposit = th.gasUsed( + await stabilityPool.withdrawFromSP(dec(3000, 18), { + from: carol, + gasPrice: GAS_PRICE, + }) + ); - // price drops: defaulter's Trove falls below MCR - await priceFeed.setPrice(dec(105, 18)); + // Check Bold balances of A, B, C have risen by the value of their compounded deposits, respectively + const alice_expectedBoldBalance = alice_Bold_Balance_Before + .add(alice_Deposit_Before) + .toString(); - // defaulter's Trove is closed. - await troveManager.liquidate(defaulter_1, { from: owner }); + const bob_expectedBoldBalance = bob_Bold_Balance_Before + .add(bob_Deposit_Before) + .toString(); + const carol_expectedBoldBalance = carol_Bold_Balance_Before + .add(carol_Deposit_Before) + .toString(); - // price bounces back - await priceFeed.setPrice(dec(200, 18)); + const alice_Bold_Balance_After = ( + await boldToken.balanceOf(alice) + ).toString(); - // Alice sends her ETH Gains to her Trove - await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); + const bob_Bold_Balance_After = ( + await boldToken.balanceOf(bob) + ).toString(); + const carol_Bold_Balance_After = ( + await boldToken.balanceOf(carol) + ).toString(); - assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); + assert.equal(alice_Bold_Balance_After, alice_expectedBoldBalance); + assert.equal(bob_Bold_Balance_After, bob_expectedBoldBalance); + assert.equal(carol_Bold_Balance_After, carol_expectedBoldBalance); - const ETHinSP_Before = (await stabilityPool.getETH()).toString(); + // Check ETH balances of A, B, C have increased by the value of their ETH gain from liquidations, respectively + const alice_expectedETHBalance = alice_ETH_Balance_Before + .add(alice_ETHGain_Before) + .toString(); + const bob_expectedETHBalance = bob_ETH_Balance_Before + .add(bob_ETHGain_Before) + .toString(); + const carol_expectedETHBalance = carol_ETH_Balance_Before + .add(carol_ETHGain_Before) + .toString(); - // Alice attempts second withdrawal from SP to Trove - reverts, due to 0 ETH Gain - const txPromise_A = stabilityPool.withdrawETHGainToTrove(alice, alice, { - from: alice, - }); - await th.assertRevert(txPromise_A); + const alice_ETHBalance_After = ( + await web3.eth.getBalance(alice) + ).toString(); + const bob_ETHBalance_After = (await web3.eth.getBalance(bob)).toString(); + const carol_ETHBalance_After = ( + await web3.eth.getBalance(carol) + ).toString(); - // Check ETH in pool does not change - const ETHinSP_1 = (await stabilityPool.getETH()).toString(); - assert.equal(ETHinSP_Before, ETHinSP_1); + // ETH balances before minus gas used + const alice_ETHBalance_After_Gas = alice_ETHBalance_After - A_GAS_Used; + const bob_ETHBalance_After_Gas = bob_ETHBalance_After - B_GAS_Used; + const carol_ETHBalance_After_Gas = carol_ETHBalance_After - C_GAS_Used; - await priceFeed.setPrice(dec(200, 18)); + assert.equal(alice_expectedETHBalance, alice_ETHBalance_After_Gas); + assert.equal(bob_expectedETHBalance, bob_ETHBalance_After_Gas); + assert.equal(carol_expectedETHBalance, carol_ETHBalance_After_Gas); - // Alice attempts third withdrawal (this time, from SP to her own account) - await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); + // Check Bold in Stability Pool has been reduced by A, B and C's compounded deposit + const expectedBoldinSP = BoldinSP_Before.sub(alice_Deposit_Before) + .sub(bob_Deposit_Before) + .sub(carol_Deposit_Before) + .toString(); + const BoldinSP_After = ( + await stabilityPool.getTotalBoldDeposits() + ).toString(); + assert.equal(BoldinSP_After, expectedBoldinSP); - // Check ETH in pool does not change - const ETHinSP_2 = (await stabilityPool.getETH()).toString(); - assert.equal(ETHinSP_Before, ETHinSP_2); + // Check ETH in SP has reduced to zero + const ETHinSP_After = (await stabilityPool.getETH()).toString(); + assert.isAtMost(th.getDifference(ETHinSP_After, "0"), 100000); }); - it("withdrawETHGainToTrove(): decreases StabilityPool ETH and increases activePool ETH", async () => { - // --- SETUP --- - // Whale deposits 185000 LUSD in StabilityPool + it("getDepositorETHGain(): depositor does not earn further ETH gains from liquidations while their compounded deposit == 0: ", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(1000000, 18)), + extraBoldAmount: toBN(dec(1, 24)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - await stabilityPool.provideToSP(dec(185000, 18), frontEnd_1, { - from: whale, + + // A, B, C open troves + await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: alice }, + }); + await openTrove({ + extraBoldAmount: toBN(dec(20000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: bob }, + }); + await openTrove({ + extraBoldAmount: toBN(dec(30000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: carol }, }); - // defaulter opened + // defaulters open troves await openTrove({ + extraBoldAmount: toBN(dec(15000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); - - // --- TEST --- - - // Alice makes deposit #1: 15000 LUSD await openTrove({ - extraLUSDAmount: toBN(dec(15000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, + extraParams: { from: defaulter_2 }, + }); + await openTrove({ + ICR: toBN(dec(2, 18)), + extraParams: { from: defaulter_3 }, }); - await stabilityPool.provideToSP(dec(15000, 18), frontEnd_1, { + + // A, B, provide 10000, 5000 Bold to SP + await stabilityPool.provideToSP(dec(10000, 18), { from: alice, }); + await stabilityPool.provideToSP(dec(5000, 18), { from: bob }); - // price drops: defaulter's Trove falls below MCR - await priceFeed.setPrice(dec(100, 18)); + //price drops + await priceFeed.setPrice(dec(105, 18)); - // defaulter's Trove is closed. - const liquidationTx = await troveManager.liquidate(defaulter_1); - const [liquidatedDebt, liquidatedColl, gasComp] = - th.getEmittedLiquidationValues(liquidationTx); + // Liquidate defaulter 1. Empties the Pool + await troveManager.liquidate(defaulter_1); + assert.isFalse(await sortedTroves.contains(defaulter_1)); - // Expect alice to be entitled to 15000/200000 of the liquidated coll - const aliceExpectedETHGain = liquidatedColl - .mul(toBN(dec(15000, 18))) - .div(toBN(dec(200000, 18))); - const aliceETHGain = await stabilityPool.getDepositorETHGain(alice); - assert.isTrue(aliceExpectedETHGain.eq(aliceETHGain)); + const BoldinSP = (await stabilityPool.getTotalBoldDeposits()).toString(); + assert.equal(BoldinSP, "0"); - // price bounces back - await priceFeed.setPrice(dec(200, 18)); + // Check Stability deposits have been fully cancelled with debt, and are now all zero + const alice_Deposit = ( + await stabilityPool.getCompoundedBoldDeposit(alice) + ).toString(); + const bob_Deposit = ( + await stabilityPool.getCompoundedBoldDeposit(bob) + ).toString(); - //check activePool and StabilityPool Ether before retrieval: - const active_ETH_Before = await activePool.getETH(); - const stability_ETH_Before = await stabilityPool.getETH(); + assert.equal(alice_Deposit, "0"); + assert.equal(bob_Deposit, "0"); - // Alice retrieves redirects ETH gain to her Trove - await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); + // Get ETH gain for A and B + const alice_ETHGain_1 = ( + await stabilityPool.getDepositorETHGain(alice) + ).toString(); + const bob_ETHGain_1 = ( + await stabilityPool.getDepositorETHGain(bob) + ).toString(); - const active_ETH_After = await activePool.getETH(); - const stability_ETH_After = await stabilityPool.getETH(); + // Whale deposits 10000 Bold to Stability Pool + await stabilityPool.provideToSP(dec(1, 24), { from: whale }); - const active_ETH_Difference = active_ETH_After.sub(active_ETH_Before); // AP ETH should increase - const stability_ETH_Difference = - stability_ETH_Before.sub(stability_ETH_After); // SP ETH should decrease + // Liquidation 2 + await troveManager.liquidate(defaulter_2); + assert.isFalse(await sortedTroves.contains(defaulter_2)); - // check Pool ETH values change by Alice's ETHGain, i.e 0.075 ETH - assert.isAtMost( - th.getDifference(active_ETH_Difference, aliceETHGain), - 10000 - ); - assert.isAtMost( - th.getDifference(stability_ETH_Difference, aliceETHGain), - 10000 - ); + // Check Alice and Bob have not received ETH gain from liquidation 2 while their deposit was 0 + const alice_ETHGain_2 = ( + await stabilityPool.getDepositorETHGain(alice) + ).toString(); + const bob_ETHGain_2 = ( + await stabilityPool.getDepositorETHGain(bob) + ).toString(); + + assert.equal(alice_ETHGain_1, alice_ETHGain_2); + assert.equal(bob_ETHGain_1, bob_ETHGain_2); + + // Liquidation 3 + await troveManager.liquidate(defaulter_3); + assert.isFalse(await sortedTroves.contains(defaulter_3)); + + // Check Alice and Bob have not received ETH gain from liquidation 3 while their deposit was 0 + const alice_ETHGain_3 = ( + await stabilityPool.getDepositorETHGain(alice) + ).toString(); + const bob_ETHGain_3 = ( + await stabilityPool.getDepositorETHGain(bob) + ).toString(); + + assert.equal(alice_ETHGain_1, alice_ETHGain_3); + assert.equal(bob_ETHGain_1, bob_ETHGain_3); }); - it("withdrawETHGainToTrove(): All depositors are able to withdraw their ETH gain from the SP to their Trove", async () => { - // Whale opens trove + it("withdrawFromSP(), full withdrawal: zero's depositor's snapshots", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // Defaulter opens trove await openTrove({ ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); - // 6 Accounts open troves and provide to SP - const depositors = [alice, bob, carol, dennis, erin, flyn]; - for (account of depositors) { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: account }, - }); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: account, - }); - } + // SETUP: Execute a series of operations to make G, S > 0 and P < 1 + + // E opens trove and makes a deposit + await openTrove({ + extraBoldAmount: toBN(dec(20000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: E }, + }); + await stabilityPool.provideToSP(dec(10000, 18), { from: E }); + + // Fast-forward time and make a second deposit + await th.fastForwardTime( + timeValues.SECONDS_IN_ONE_HOUR, + web3.currentProvider + ); + await stabilityPool.provideToSP(dec(10000, 18), { from: E }); + // perform a liquidation to make 0 < P < 1, and S > 0 await priceFeed.setPrice(dec(105, 18)); + assert.isFalse(await th.checkRecoveryMode(contracts)); + await troveManager.liquidate(defaulter_1); - // price bounces back + const currentEpoch = await stabilityPool.currentEpoch(); + const currentScale = await stabilityPool.currentScale(); + + const S_Before = await stabilityPool.epochToScaleToSum( + currentEpoch, + currentScale + ); + const P_Before = await stabilityPool.P(); + + // Confirm 0 < P < 1 + assert.isTrue(P_Before.gt(toBN("0")) && P_Before.lt(toBN(dec(1, 18)))); + // Confirm S, G are both > 0 + assert.isTrue(S_Before.gt(toBN("0"))); + + // --- TEST --- + + // Whale transfers to A, B + await boldToken.transfer(A, dec(10000, 18), { from: whale }); + await boldToken.transfer(B, dec(20000, 18), { from: whale }); + await priceFeed.setPrice(dec(200, 18)); - // All depositors attempt to withdraw - const tx1 = await stabilityPool.withdrawETHGainToTrove(alice, alice, { - from: alice, - }); - assert.isTrue(tx1.receipt.status); - const tx2 = await stabilityPool.withdrawETHGainToTrove(bob, bob, { - from: bob, - }); - assert.isTrue(tx1.receipt.status); - const tx3 = await stabilityPool.withdrawETHGainToTrove(carol, carol, { - from: carol, + // C, D open troves + await openTrove({ + extraBoldAmount: toBN(dec(30000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: C }, }); - assert.isTrue(tx1.receipt.status); - const tx4 = await stabilityPool.withdrawETHGainToTrove(dennis, dennis, { - from: dennis, + await openTrove({ + extraBoldAmount: toBN(dec(40000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: D }, }); - assert.isTrue(tx1.receipt.status); - const tx5 = await stabilityPool.withdrawETHGainToTrove(erin, erin, { - from: erin, + + // A, B, C, D make their initial deposits + await stabilityPool.provideToSP(dec(10000, 18), { from: A }); + await stabilityPool.provideToSP(dec(20000, 18), { + from: B, }); - assert.isTrue(tx1.receipt.status); - const tx6 = await stabilityPool.withdrawETHGainToTrove(flyn, flyn, { - from: flyn, + await stabilityPool.provideToSP(dec(30000, 18), { from: C }); + await stabilityPool.provideToSP(dec(40000, 18), { + from: D, }); - assert.isTrue(tx1.receipt.status); + + // Check deposits snapshots are non-zero + + for (depositor of [A, B, C, D]) { + const snapshot = await stabilityPool.depositSnapshots(depositor); + + const ZERO = toBN("0"); + // Check S,P, G snapshots are non-zero + assert.isTrue(snapshot[0].eq(S_Before)); // S + assert.isTrue(snapshot[1].eq(P_Before)); // P + assert.equal(snapshot[3], "0"); // scale + assert.equal(snapshot[4], "0"); // epoch + } + + // All depositors make full withdrawal + await stabilityPool.withdrawFromSP(dec(10000, 18), { from: A }); + await stabilityPool.withdrawFromSP(dec(20000, 18), { from: B }); + await stabilityPool.withdrawFromSP(dec(30000, 18), { from: C }); + await stabilityPool.withdrawFromSP(dec(40000, 18), { from: D }); + + // Check all depositors' snapshots have been zero'd + for (depositor of [A, B, C, D]) { + const snapshot = await stabilityPool.depositSnapshots(depositor); + + // Check S, P, G snapshots are now zero + assert.equal(snapshot[0], "0"); // S + assert.equal(snapshot[1], "0"); // P + assert.equal(snapshot[2], "0"); // G + assert.equal(snapshot[3], "0"); // scale + assert.equal(snapshot[4], "0"); // epoch + } }); - it("withdrawETHGainToTrove(): All depositors withdraw, each withdraw their correct ETH gain", async () => { - // Whale opens trove + it("withdrawFromSP(), reverts when initial deposit value is 0", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), + extraBoldAmount: toBN(dec(100000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // defaulter opened + // A opens trove and join the Stability Pool await openTrove({ + extraBoldAmount: toBN(dec(10100, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + extraParams: { from: A }, }); + await stabilityPool.provideToSP(dec(10000, 18), { from: A }); - // 6 Accounts open troves and provide to SP - const depositors = [alice, bob, carol, dennis, erin, flyn]; - for (account of depositors) { - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: account }, - }); - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: account, - }); - } - const collBefore = (await troveManager.Troves(alice))[1]; // all troves have same coll before + await openTrove({ + ICR: toBN(dec(2, 18)), + extraParams: { from: defaulter_1 }, + }); - await priceFeed.setPrice(dec(105, 18)); - const liquidationTx = await troveManager.liquidate(defaulter_1); - const [, liquidatedColl, ,] = - th.getEmittedLiquidationValues(liquidationTx); + // SETUP: Execute a series of operations to trigger ETH rewards for depositor A - /* All depositors attempt to withdraw their ETH gain to their Trove. Each depositor - receives (liquidatedColl/ 6). + // Fast-forward time and make a second deposit + await th.fastForwardTime( + timeValues.SECONDS_IN_ONE_HOUR, + web3.currentProvider + ); + await stabilityPool.provideToSP(dec(100, 18), { from: A }); - Thus, expected new collateral for each depositor with 1 Ether in their trove originally, is - (1 + liquidatedColl/6) - */ + // perform a liquidation to make 0 < P < 1, and S > 0 + await priceFeed.setPrice(dec(105, 18)); + assert.isFalse(await th.checkRecoveryMode(contracts)); - const expectedCollGain = liquidatedColl.div(toBN("6")); + await troveManager.liquidate(defaulter_1); + assert.isFalse(await sortedTroves.contains(defaulter_1)); await priceFeed.setPrice(dec(200, 18)); - await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); - const aliceCollAfter = (await troveManager.Troves(alice))[1]; - assert.isAtMost( - th.getDifference(aliceCollAfter.sub(collBefore), expectedCollGain), - 10000 - ); + // A successfully withraws deposit and all gains + await stabilityPool.withdrawFromSP(dec(10100, 18), { from: A }); - await stabilityPool.withdrawETHGainToTrove(bob, bob, { from: bob }); - const bobCollAfter = (await troveManager.Troves(bob))[1]; - assert.isAtMost( - th.getDifference(bobCollAfter.sub(collBefore), expectedCollGain), - 10000 - ); + // Confirm A's recorded deposit is 0 + const A_deposit = (await stabilityPool.deposits(A)); // get initialValue property on deposit struct + assert.equal(A_deposit, "0"); - await stabilityPool.withdrawETHGainToTrove(carol, carol, { from: carol }); - const carolCollAfter = (await troveManager.Troves(carol))[1]; - assert.isAtMost( - th.getDifference(carolCollAfter.sub(collBefore), expectedCollGain), - 10000 - ); + // --- TEST --- + const expectedRevertMessage = + "StabilityPool: User must have a non-zero deposit"; - await stabilityPool.withdrawETHGainToTrove(dennis, dennis, { - from: dennis, + // Further withdrawal attempt from A + const withdrawalPromise_A = stabilityPool.withdrawFromSP(dec(10000, 18), { + from: A, }); - const dennisCollAfter = (await troveManager.Troves(dennis))[1]; - assert.isAtMost( - th.getDifference(dennisCollAfter.sub(collBefore), expectedCollGain), - 10000 - ); - - await stabilityPool.withdrawETHGainToTrove(erin, erin, { from: erin }); - const erinCollAfter = (await troveManager.Troves(erin))[1]; - assert.isAtMost( - th.getDifference(erinCollAfter.sub(collBefore), expectedCollGain), - 10000 - ); + await th.assertRevert(withdrawalPromise_A, expectedRevertMessage); - await stabilityPool.withdrawETHGainToTrove(flyn, flyn, { from: flyn }); - const flynCollAfter = (await troveManager.Troves(flyn))[1]; - assert.isAtMost( - th.getDifference(flynCollAfter.sub(collBefore), expectedCollGain), - 10000 - ); + // Withdrawal attempt of a non-existent deposit, from C + const withdrawalPromise_C = stabilityPool.withdrawFromSP(dec(10000, 18), { + from: C, + }); + await th.assertRevert(withdrawalPromise_C, expectedRevertMessage); }); - it("withdrawETHGainToTrove(): caller can withdraw full deposit and ETH gain to their trove during Recovery Mode", async () => { - // --- SETUP --- + // --- withdrawETHGainToTrove --- - // Defaulter opens + it("withdrawETHGainToTrove(): reverts when user has no active deposit", async () => { await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + extraBoldAmount: toBN(dec(100000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, }); - // A, B, C open troves await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: bob }, }); + + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, + }); + + const alice_initialDeposit = ( + await stabilityPool.deposits(alice) + ).toString(); + const bob_initialDeposit = ( + await stabilityPool.deposits(bob) + ).toString(); + + assert.equal(alice_initialDeposit, dec(10000, 18)); + assert.equal(bob_initialDeposit, "0"); + + // Defaulter opens a trove, price drops, defaulter gets liquidated await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, + extraParams: { from: defaulter_1 }, }); + await priceFeed.setPrice(dec(105, 18)); + assert.isFalse(await th.checkRecoveryMode(contracts)); + await troveManager.liquidate(defaulter_1); + assert.isFalse(await sortedTroves.contains(defaulter_1)); - // A, B, C provides 10000, 5000, 3000 LUSD to SP - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { + const txAlice = await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice, }); - await stabilityPool.provideToSP(dec(5000, 18), frontEnd_1, { from: bob }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_1, { - from: carol, + assert.isTrue(txAlice.receipt.status); + + const txPromise_B = stabilityPool.withdrawETHGainToTrove(bob, bob, { + from: bob, }); + await th.assertRevert(txPromise_B); + }); - assert.isFalse(await th.checkRecoveryMode(contracts)); + it("withdrawETHGainToTrove(): Applies BoldLoss to user's deposit, and redirects ETH reward to user's Trove", async () => { + // --- SETUP --- + // Whale deposits 185000 Bold in StabilityPool + await openTrove({ + extraBoldAmount: toBN(dec(1000000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: whale }, + }); + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, + }); - // Price drops to 105, - await priceFeed.setPrice(dec(105, 18)); - const price = await priceFeed.getPrice(); + // Defaulter opens trove + await openTrove({ + ICR: toBN(dec(2, 18)), + extraParams: { from: defaulter_1 }, + }); - assert.isTrue(await th.checkRecoveryMode(contracts)); + // --- TEST --- - // Check defaulter 1 has ICR: 100% < ICR < 110%. - assert.isTrue( - await th.ICRbetween100and110(defaulter_1, troveManager, price) - ); + // Alice makes deposit #1: 15000 Bold + await openTrove({ + extraBoldAmount: toBN(dec(15000, 18)), + ICR: toBN(dec(10, 18)), + extraParams: { from: alice }, + }); + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, + }); - const alice_Collateral_Before = (await troveManager.Troves(alice))[1]; - const bob_Collateral_Before = (await troveManager.Troves(bob))[1]; - const carol_Collateral_Before = (await troveManager.Troves(carol))[1]; + // check Alice's Trove recorded ETH Before: + const aliceTrove_Before = await troveManager.Troves(alice); + const aliceTrove_ETH_Before = aliceTrove_Before[1]; + assert.isTrue(aliceTrove_ETH_Before.gt(toBN("0"))); - // Liquidate defaulter 1 - assert.isTrue(await sortedTroves.contains(defaulter_1)); - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); + // price drops: defaulter's Trove falls below MCR, alice and whale Trove remain active + await priceFeed.setPrice(dec(105, 18)); - const alice_ETHGain_Before = await stabilityPool.getDepositorETHGain( + // Defaulter's Trove is closed + const liquidationTx_1 = await troveManager.liquidate(defaulter_1, { + from: owner, + }); + const [liquidatedDebt, liquidatedColl, ,] = + th.getEmittedLiquidationValues(liquidationTx_1); + + const ETHGain_A = await stabilityPool.getDepositorETHGain(alice); + const compoundedDeposit_A = await stabilityPool.getCompoundedBoldDeposit( alice ); - const bob_ETHGain_Before = await stabilityPool.getDepositorETHGain(bob); - const carol_ETHGain_Before = await stabilityPool.getDepositorETHGain( - carol + + // Alice should receive rewards proportional to her deposit as share of total deposits + const expectedETHGain_A = liquidatedColl + .mul(toBN(dec(15000, 18))) + .div(toBN(dec(200000, 18))); + const expectedBoldLoss_A = liquidatedDebt + .mul(toBN(dec(15000, 18))) + .div(toBN(dec(200000, 18))); + const expectedCompoundedDeposit_A = toBN(dec(15000, 18)).sub( + expectedBoldLoss_A ); - // A, B, C withdraw their full ETH gain from the Stability Pool to their trove + assert.isAtMost( + th.getDifference(expectedCompoundedDeposit_A, compoundedDeposit_A), + 100000 + ); + + // Alice sends her ETH Gains to her Trove await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); - await stabilityPool.withdrawETHGainToTrove(bob, bob, { from: bob }); - await stabilityPool.withdrawETHGainToTrove(carol, carol, { from: carol }); - // Check collateral of troves A, B, C has increased by the value of their ETH gain from liquidations, respectively - const alice_expectedCollateral = alice_Collateral_Before - .add(alice_ETHGain_Before) - .toString(); - const bob_expectedColalteral = bob_Collateral_Before - .add(bob_ETHGain_Before) - .toString(); - const carol_expectedCollateral = carol_Collateral_Before - .add(carol_ETHGain_Before) - .toString(); + // check Alice's BoldLoss has been applied to her deposit expectedCompoundedDeposit_A + alice_deposit_afterDefault = (await stabilityPool.deposits(alice)); + assert.isAtMost( + th.getDifference( + alice_deposit_afterDefault, + expectedCompoundedDeposit_A + ), + 100000 + ); - const alice_Collateral_After = (await troveManager.Troves(alice))[1]; - const bob_Collateral_After = (await troveManager.Troves(bob))[1]; - const carol_Collateral_After = (await troveManager.Troves(carol))[1]; + // check alice's Trove recorded ETH has increased by the expected reward amount + const aliceTrove_After = await troveManager.Troves(alice); + const aliceTrove_ETH_After = aliceTrove_After[1]; - assert.equal(alice_expectedCollateral, alice_Collateral_After); - assert.equal(bob_expectedColalteral, bob_Collateral_After); - assert.equal(carol_expectedCollateral, carol_Collateral_After); + const Trove_ETH_Increase = aliceTrove_ETH_After + .sub(aliceTrove_ETH_Before) + .toString(); - // Check ETH in SP has reduced to zero - const ETHinSP_After = (await stabilityPool.getETH()).toString(); - assert.isAtMost(th.getDifference(ETHinSP_After, "0"), 100000); + assert.equal(Trove_ETH_Increase, ETHGain_A); }); - it("withdrawETHGainToTrove(): reverts if user has no trove", async () => { + it("withdrawETHGainToTrove(): reverts if it would leave trove with ICR < MCR", async () => { + // --- SETUP --- + // Whale deposits 1850 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: alice }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: bob }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: carol }, + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, }); - // Defaulter opens + // defaulter opened await openTrove({ ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); - // A transfers LUSD to D - await boldToken.transfer(dennis, dec(10000, 18), { from: alice }); + // --- TEST --- - // D deposits to Stability Pool - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { - from: dennis, + // Alice makes deposit #1: 15000 Bold + await openTrove({ + extraBoldAmount: toBN(dec(15000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: alice }, + }); + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - //Price drops - await priceFeed.setPrice(dec(105, 18)); + // check alice's Trove recorded ETH Before: + const aliceTrove_Before = await troveManager.Troves(alice); + const aliceTrove_ETH_Before = aliceTrove_Before[1]; + assert.isTrue(aliceTrove_ETH_Before.gt(toBN("0"))); - //Liquidate defaulter 1 - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); + // price drops: defaulter's Trove falls below MCR + await priceFeed.setPrice(dec(10, 18)); - await priceFeed.setPrice(dec(200, 18)); + // defaulter's Trove is closed. + await troveManager.liquidate(defaulter_1, { from: owner }); - // D attempts to withdraw his ETH gain to Trove - await th.assertRevert( - stabilityPool.withdrawETHGainToTrove(dennis, dennis, { from: dennis }), - "caller must have an active trove to withdraw ETHGain to" + // Alice attempts to her ETH Gains to her Trove + await assertRevert( + stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }), + "BorrowerOps: An operation that would result in ICR < MCR is not permitted" ); }); - it("withdrawETHGainToTrove(): triggers LQTY reward event - increases the sum G", async () => { + it("withdrawETHGainToTrove(): Subsequent deposit and withdrawal attempt from same account, with no intermediate liquidations, withdraws zero ETH", async () => { + // --- SETUP --- + // Whale deposits 1850 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, + }); - // A, B, C open troves + // defaulter opened await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: defaulter_1 }, }); + + // --- TEST --- + + // Alice makes deposit #1: 15000 Bold await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), + extraBoldAmount: toBN(dec(15000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: alice }, }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - // A and B provide to SP - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(10000, 18), ZERO_ADDRESS, { - from: B, - }); + // check alice's Trove recorded ETH Before: + const aliceTrove_Before = await troveManager.Troves(alice); + const aliceTrove_ETH_Before = aliceTrove_Before[1]; + assert.isTrue(aliceTrove_ETH_Before.gt(toBN("0"))); - // Defaulter opens a trove, price drops, defaulter gets liquidated - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); + // price drops: defaulter's Trove falls below MCR await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - const G_Before = await stabilityPool.epochToScaleToG(0, 0); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // defaulter's Trove is closed. + await troveManager.liquidate(defaulter_1, { from: owner }); + // price bounces back await priceFeed.setPrice(dec(200, 18)); - // A withdraws from SP - await stabilityPool.withdrawFromSP(dec(50, 18), { from: A }); + // Alice sends her ETH Gains to her Trove + await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); - const G_1 = await stabilityPool.epochToScaleToG(0, 0); + assert.equal(await stabilityPool.getDepositorETHGain(alice), 0); - // Expect G has increased from the LQTY reward event triggered - assert.isTrue(G_1.gt(G_Before)); + const ETHinSP_Before = (await stabilityPool.getETH()).toString(); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // Alice attempts second withdrawal from SP to Trove - reverts, due to 0 ETH Gain + const txPromise_A = stabilityPool.withdrawETHGainToTrove(alice, alice, { + from: alice, + }); + await th.assertRevert(txPromise_A); - // Check B has non-zero ETH gain - assert.isTrue((await stabilityPool.getDepositorETHGain(B)).gt(ZERO)); + // Check ETH in pool does not change + const ETHinSP_1 = (await stabilityPool.getETH()).toString(); + assert.equal(ETHinSP_Before, ETHinSP_1); - // B withdraws to trove - await stabilityPool.withdrawETHGainToTrove(B, B, { from: B }); + await priceFeed.setPrice(dec(200, 18)); - const G_2 = await stabilityPool.epochToScaleToG(0, 0); + // Alice attempts third withdrawal (this time, from SP to her own account) + await stabilityPool.withdrawFromSP(dec(15000, 18), { from: alice }); - // Expect G has increased from the LQTY reward event triggered - assert.isTrue(G_2.gt(G_1)); + // Check ETH in pool does not change + const ETHinSP_2 = (await stabilityPool.getETH()).toString(); + assert.equal(ETHinSP_Before, ETHinSP_2); }); - it("withdrawETHGainToTrove(), partial withdrawal: doesn't change the front end tag", async () => { + it("withdrawETHGainToTrove(): decreases StabilityPool ETH and increases activePool ETH", async () => { + // --- SETUP --- + // Whale deposits 185000 Bold in StabilityPool await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(1000000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + await stabilityPool.provideToSP(dec(185000, 18), { + from: whale, }); + + // defaulter opened await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_1 }, }); - // A, B, C, D, E provide to SP - await stabilityPool.provideToSP(dec(10000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20000, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(30000, 18), ZERO_ADDRESS, { - from: C, - }); + // --- TEST --- - // Defaulter opens a trove, price drops, defaulter gets liquidated + // Alice makes deposit #1: 15000 Bold await openTrove({ + extraBoldAmount: toBN(dec(15000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + extraParams: { from: alice }, + }); + await stabilityPool.provideToSP(dec(15000, 18), { + from: alice, }); - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // price drops: defaulter's Trove falls below MCR + await priceFeed.setPrice(dec(100, 18)); + + // defaulter's Trove is closed. + const liquidationTx = await troveManager.liquidate(defaulter_1); + const [liquidatedDebt, liquidatedColl, gasComp] = + th.getEmittedLiquidationValues(liquidationTx); - // Check A, B, C have non-zero ETH gain - assert.isTrue((await stabilityPool.getDepositorETHGain(A)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(B)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(C)).gt(ZERO)); + // Expect alice to be entitled to 15000/200000 of the liquidated coll + const aliceExpectedETHGain = liquidatedColl + .mul(toBN(dec(15000, 18))) + .div(toBN(dec(200000, 18))); + const aliceETHGain = await stabilityPool.getDepositorETHGain(alice); + assert.isTrue(aliceExpectedETHGain.eq(aliceETHGain)); + // price bounces back await priceFeed.setPrice(dec(200, 18)); - // A, B, C withdraw to trove - await stabilityPool.withdrawETHGainToTrove(A, A, { from: A }); - await stabilityPool.withdrawETHGainToTrove(B, B, { from: B }); - await stabilityPool.withdrawETHGainToTrove(C, C, { from: C }); + //check activePool and StabilityPool Ether before retrieval: + const active_ETH_Before = await activePool.getETH(); + const stability_ETH_Before = await stabilityPool.getETH(); + + // Alice retrieves redirects ETH gain to her Trove + await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); + + const active_ETH_After = await activePool.getETH(); + const stability_ETH_After = await stabilityPool.getETH(); - const frontEndTag_A = (await stabilityPool.deposits(A))[1]; - const frontEndTag_B = (await stabilityPool.deposits(B))[1]; - const frontEndTag_C = (await stabilityPool.deposits(C))[1]; + const active_ETH_Difference = active_ETH_After.sub(active_ETH_Before); // AP ETH should increase + const stability_ETH_Difference = + stability_ETH_Before.sub(stability_ETH_After); // SP ETH should decrease - // Check deposits are still tagged with their original front end - assert.equal(frontEndTag_A, frontEnd_1); - assert.equal(frontEndTag_B, frontEnd_2); - assert.equal(frontEndTag_C, ZERO_ADDRESS); + // check Pool ETH values change by Alice's ETHGain, i.e 0.075 ETH + assert.isAtMost( + th.getDifference(active_ETH_Difference, aliceETHGain), + 10000 + ); + assert.isAtMost( + th.getDifference(stability_ETH_Difference, aliceETHGain), + 10000 + ); }); - it("withdrawETHGainToTrove(), eligible deposit: depositor receives LQTY rewards", async () => { + it("withdrawETHGainToTrove(): All depositors are able to withdraw their ETH gain from the SP to their Trove", async () => { + // Whale opens trove await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(100000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); + // Defaulter opens trove await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_1 }, }); - // A, B, C, provide to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(3000, 18), ZERO_ADDRESS, { from: C }); - - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // 6 Accounts open troves and provide to SP + const depositors = [alice, bob, carol, dennis, erin, flyn]; + for (account of depositors) { + await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: account }, + }); + await stabilityPool.provideToSP(dec(10000, 18), { + from: account, + }); + } - // Defaulter opens a trove, price drops, defaulter gets liquidated - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - // Get A, B, C LQTY balance before - const A_LQTYBalance_Before = await lqtyToken.balanceOf(A); - const B_LQTYBalance_Before = await lqtyToken.balanceOf(B); - const C_LQTYBalance_Before = await lqtyToken.balanceOf(C); - - // Check A, B, C have non-zero ETH gain - assert.isTrue((await stabilityPool.getDepositorETHGain(A)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(B)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(C)).gt(ZERO)); + // price bounces back await priceFeed.setPrice(dec(200, 18)); - // A, B, C withdraw to trove - await stabilityPool.withdrawETHGainToTrove(A, A, { from: A }); - await stabilityPool.withdrawETHGainToTrove(B, B, { from: B }); - await stabilityPool.withdrawETHGainToTrove(C, C, { from: C }); - - // Get LQTY balance after - const A_LQTYBalance_After = await lqtyToken.balanceOf(A); - const B_LQTYBalance_After = await lqtyToken.balanceOf(B); - const C_LQTYBalance_After = await lqtyToken.balanceOf(C); - - // Check LQTY Balance of A, B, C has increased - assert.isTrue(A_LQTYBalance_After.gt(A_LQTYBalance_Before)); - assert.isTrue(B_LQTYBalance_After.gt(B_LQTYBalance_Before)); - assert.isTrue(C_LQTYBalance_After.gt(C_LQTYBalance_Before)); + // All depositors attempt to withdraw + const tx1 = await stabilityPool.withdrawETHGainToTrove(alice, alice, { + from: alice, + }); + assert.isTrue(tx1.receipt.status); + const tx2 = await stabilityPool.withdrawETHGainToTrove(bob, bob, { + from: bob, + }); + assert.isTrue(tx1.receipt.status); + const tx3 = await stabilityPool.withdrawETHGainToTrove(carol, carol, { + from: carol, + }); + assert.isTrue(tx1.receipt.status); + const tx4 = await stabilityPool.withdrawETHGainToTrove(dennis, dennis, { + from: dennis, + }); + assert.isTrue(tx1.receipt.status); + const tx5 = await stabilityPool.withdrawETHGainToTrove(erin, erin, { + from: erin, + }); + assert.isTrue(tx1.receipt.status); + const tx6 = await stabilityPool.withdrawETHGainToTrove(flyn, flyn, { + from: flyn, + }); + assert.isTrue(tx1.receipt.status); }); - it("withdrawETHGainToTrove(), eligible deposit: tagged front end receives LQTY rewards", async () => { + it("withdrawETHGainToTrove(): All depositors withdraw, each withdraw their correct ETH gain", async () => { + // Whale opens trove await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(100000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // A, B, C open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: B }, - }); + // defaulter opened await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: defaulter_1 }, }); - // A, B, C, provide to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_3, { from: C }); + // 6 Accounts open troves and provide to SP + const depositors = [alice, bob, carol, dennis, erin, flyn]; + for (account of depositors) { + await openTrove({ + extraBoldAmount: toBN(dec(10000, 18)), + ICR: toBN(dec(2, 18)), + extraParams: { from: account }, + }); + await stabilityPool.provideToSP(dec(10000, 18), { + from: account, + }); + } + const collBefore = (await troveManager.Troves(alice))[1]; // all troves have same coll before - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider + await priceFeed.setPrice(dec(105, 18)); + const liquidationTx = await troveManager.liquidate(defaulter_1); + const [, liquidatedColl, ,] = + th.getEmittedLiquidationValues(liquidationTx); + + /* All depositors attempt to withdraw their ETH gain to their Trove. Each depositor + receives (liquidatedColl/ 6). + + Thus, expected new collateral for each depositor with 1 Ether in their trove originally, is + (1 + liquidatedColl/6) + */ + + const expectedCollGain = liquidatedColl.div(toBN("6")); + + await priceFeed.setPrice(dec(200, 18)); + + await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); + const aliceCollAfter = (await troveManager.Troves(alice))[1]; + assert.isAtMost( + th.getDifference(aliceCollAfter.sub(collBefore), expectedCollGain), + 10000 + ); + + await stabilityPool.withdrawETHGainToTrove(bob, bob, { from: bob }); + const bobCollAfter = (await troveManager.Troves(bob))[1]; + assert.isAtMost( + th.getDifference(bobCollAfter.sub(collBefore), expectedCollGain), + 10000 + ); + + await stabilityPool.withdrawETHGainToTrove(carol, carol, { from: carol }); + const carolCollAfter = (await troveManager.Troves(carol))[1]; + assert.isAtMost( + th.getDifference(carolCollAfter.sub(collBefore), expectedCollGain), + 10000 ); - // Defaulter opens a trove, price drops, defaulter gets liquidated - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, + await stabilityPool.withdrawETHGainToTrove(dennis, dennis, { + from: dennis, }); - await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); - await troveManager.liquidate(defaulter_1); - assert.isFalse(await sortedTroves.contains(defaulter_1)); - - // Get front ends' LQTY balance before - const F1_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_1); - const F2_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_2); - const F3_LQTYBalance_Before = await lqtyToken.balanceOf(frontEnd_3); + const dennisCollAfter = (await troveManager.Troves(dennis))[1]; + assert.isAtMost( + th.getDifference(dennisCollAfter.sub(collBefore), expectedCollGain), + 10000 + ); - await priceFeed.setPrice(dec(200, 18)); + await stabilityPool.withdrawETHGainToTrove(erin, erin, { from: erin }); + const erinCollAfter = (await troveManager.Troves(erin))[1]; + assert.isAtMost( + th.getDifference(erinCollAfter.sub(collBefore), expectedCollGain), + 10000 + ); - // Check A, B, C have non-zero ETH gain - assert.isTrue((await stabilityPool.getDepositorETHGain(A)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(B)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(C)).gt(ZERO)); - - // A, B, C withdraw - await stabilityPool.withdrawETHGainToTrove(A, A, { from: A }); - await stabilityPool.withdrawETHGainToTrove(B, B, { from: B }); - await stabilityPool.withdrawETHGainToTrove(C, C, { from: C }); - - // Get front ends' LQTY balance after - const F1_LQTYBalance_After = await lqtyToken.balanceOf(frontEnd_1); - const F2_LQTYBalance_After = await lqtyToken.balanceOf(frontEnd_2); - const F3_LQTYBalance_After = await lqtyToken.balanceOf(frontEnd_3); - - // Check LQTY Balance of front ends has increased - assert.isTrue(F1_LQTYBalance_After.gt(F1_LQTYBalance_Before)); - assert.isTrue(F2_LQTYBalance_After.gt(F2_LQTYBalance_Before)); - assert.isTrue(F3_LQTYBalance_After.gt(F3_LQTYBalance_Before)); + await stabilityPool.withdrawETHGainToTrove(flyn, flyn, { from: flyn }); + const flynCollAfter = (await troveManager.Troves(flyn))[1]; + assert.isAtMost( + th.getDifference(flynCollAfter.sub(collBefore), expectedCollGain), + 10000 + ); }); - it("withdrawETHGainToTrove(), eligible deposit: tagged front end's stake decreases", async () => { - await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), - ICR: toBN(dec(10, 18)), - extraParams: { from: whale }, - }); + it("withdrawETHGainToTrove(): caller can withdraw full deposit and ETH gain to their trove during Recovery Mode", async () => { + // --- SETUP --- - // A, B, C, D, E, F open troves + // Defaulter opens await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: A }, + extraParams: { from: defaulter_1 }, }); + + // A, B, C open troves await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: bob }, }); await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + extraParams: { from: carol }, }); - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: E }, + + // A, B, C provides 10000, 5000, 3000 Bold to SP + await stabilityPool.provideToSP(dec(10000, 18), { + from: alice, }); - await openTrove({ - extraLUSDAmount: toBN(dec(30000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: F }, + await stabilityPool.provideToSP(dec(5000, 18), { from: bob }); + await stabilityPool.provideToSP(dec(3000, 18), { + from: carol, }); - // A, B, C, D, E, F provide to SP - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_2, { from: B }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_3, { from: C }); - await stabilityPool.provideToSP(dec(1000, 18), frontEnd_1, { from: D }); - await stabilityPool.provideToSP(dec(2000, 18), frontEnd_2, { from: E }); - await stabilityPool.provideToSP(dec(3000, 18), frontEnd_3, { from: F }); + assert.isFalse(await th.checkRecoveryMode(contracts)); - // Defaulter opens a trove, price drops, defaulter gets liquidated - await openTrove({ - ICR: toBN(dec(2, 18)), - extraParams: { from: defaulter_1 }, - }); + // Price drops to 105, await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); + const price = await priceFeed.getPrice(); + + assert.isTrue(await th.checkRecoveryMode(contracts)); + + // Check defaulter 1 has ICR: 100% < ICR < 110%. + assert.isTrue( + await th.ICRbetween100and110(defaulter_1, troveManager, price) + ); + + const alice_Collateral_Before = (await troveManager.Troves(alice))[1]; + const bob_Collateral_Before = (await troveManager.Troves(bob))[1]; + const carol_Collateral_Before = (await troveManager.Troves(carol))[1]; + + // Liquidate defaulter 1 + assert.isTrue(await sortedTroves.contains(defaulter_1)); await troveManager.liquidate(defaulter_1); assert.isFalse(await sortedTroves.contains(defaulter_1)); - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider + const alice_ETHGain_Before = await stabilityPool.getDepositorETHGain( + alice + ); + const bob_ETHGain_Before = await stabilityPool.getDepositorETHGain(bob); + const carol_ETHGain_Before = await stabilityPool.getDepositorETHGain( + carol ); - // Get front ends' stake before - const F1_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_Before = await stabilityPool.frontEndStakes(frontEnd_3); + // A, B, C withdraw their full ETH gain from the Stability Pool to their trove + await stabilityPool.withdrawETHGainToTrove(alice, alice, { from: alice }); + await stabilityPool.withdrawETHGainToTrove(bob, bob, { from: bob }); + await stabilityPool.withdrawETHGainToTrove(carol, carol, { from: carol }); - await priceFeed.setPrice(dec(200, 18)); + // Check collateral of troves A, B, C has increased by the value of their ETH gain from liquidations, respectively + const alice_expectedCollateral = alice_Collateral_Before + .add(alice_ETHGain_Before) + .toString(); + const bob_expectedColalteral = bob_Collateral_Before + .add(bob_ETHGain_Before) + .toString(); + const carol_expectedCollateral = carol_Collateral_Before + .add(carol_ETHGain_Before) + .toString(); + + const alice_Collateral_After = (await troveManager.Troves(alice))[1]; + const bob_Collateral_After = (await troveManager.Troves(bob))[1]; + const carol_Collateral_After = (await troveManager.Troves(carol))[1]; + + assert.equal(alice_expectedCollateral, alice_Collateral_After); + assert.equal(bob_expectedColalteral, bob_Collateral_After); + assert.equal(carol_expectedCollateral, carol_Collateral_After); - // Check A, B, C have non-zero ETH gain - assert.isTrue((await stabilityPool.getDepositorETHGain(A)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(B)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(C)).gt(ZERO)); - - // A, B, C withdraw to trove - await stabilityPool.withdrawETHGainToTrove(A, A, { from: A }); - await stabilityPool.withdrawETHGainToTrove(B, B, { from: B }); - await stabilityPool.withdrawETHGainToTrove(C, C, { from: C }); - - // Get front ends' stakes after - const F1_Stake_After = await stabilityPool.frontEndStakes(frontEnd_1); - const F2_Stake_After = await stabilityPool.frontEndStakes(frontEnd_2); - const F3_Stake_After = await stabilityPool.frontEndStakes(frontEnd_3); - - // Check front ends' stakes have decreased - assert.isTrue(F1_Stake_After.lt(F1_Stake_Before)); - assert.isTrue(F2_Stake_After.lt(F2_Stake_Before)); - assert.isTrue(F3_Stake_After.lt(F3_Stake_Before)); + // Check ETH in SP has reduced to zero + const ETHinSP_After = (await stabilityPool.getETH()).toString(); + assert.isAtMost(th.getDifference(ETHinSP_After, "0"), 100000); }); - it("withdrawETHGainToTrove(), eligible deposit: tagged front end's snapshots update", async () => { + it("withdrawETHGainToTrove(): reverts if user has no trove", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // A, B, C, open troves - await openTrove({ - extraLUSDAmount: toBN(dec(20000, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: A }, - }); + // A, B, C open troves await openTrove({ - extraLUSDAmount: toBN(dec(40000, 18)), + extraBoldAmount: toBN(dec(10000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: B }, + extraParams: { from: alice }, }); await openTrove({ - extraLUSDAmount: toBN(dec(60000, 18)), + extraBoldAmount: toBN(dec(20000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: C }, + extraParams: { from: bob }, }); - - // D opens trove await openTrove({ - extraLUSDAmount: toBN(dec(10000, 18)), + extraBoldAmount: toBN(dec(30000, 18)), ICR: toBN(dec(2, 18)), - extraParams: { from: D }, + extraParams: { from: carol }, }); + // Defaulter opens await openTrove({ ICR: toBN(dec(2, 18)), extraParams: { from: defaulter_1 }, }); - // --- SETUP --- - - const deposit_A = dec(100, 18); - const deposit_B = dec(200, 18); - const deposit_C = dec(300, 18); - - // A, B, C make their initial deposits - await stabilityPool.provideToSP(deposit_A, frontEnd_1, { from: A }); - await stabilityPool.provideToSP(deposit_B, frontEnd_2, { from: B }); - await stabilityPool.provideToSP(deposit_C, frontEnd_3, { from: C }); - - // fastforward time then make an SP deposit, to make G > 0 - await th.fastForwardTime( - timeValues.SECONDS_IN_ONE_HOUR, - web3.currentProvider - ); + // A transfers Bold to D + await boldToken.transfer(dennis, dec(10000, 18), { from: alice }); - await stabilityPool.provideToSP(dec(10000, 18), ZERO_ADDRESS, { - from: D, + // D deposits to Stability Pool + await stabilityPool.provideToSP(dec(10000, 18), { + from: dennis, }); - // perform a liquidation to make 0 < P < 1, and S > 0 + //Price drops await priceFeed.setPrice(dec(105, 18)); - assert.isFalse(await th.checkRecoveryMode(contracts)); + //Liquidate defaulter 1 await troveManager.liquidate(defaulter_1); - - const currentEpoch = await stabilityPool.currentEpoch(); - const currentScale = await stabilityPool.currentScale(); - - const S_Before = await stabilityPool.epochToScaleToSum( - currentEpoch, - currentScale - ); - const P_Before = await stabilityPool.P(); - const G_Before = await stabilityPool.epochToScaleToG( - currentEpoch, - currentScale - ); - - // Confirm 0 < P < 1 - assert.isTrue(P_Before.gt(toBN("0")) && P_Before.lt(toBN(dec(1, 18)))); - // Confirm S, G are both > 0 - assert.isTrue(S_Before.gt(toBN("0"))); - assert.isTrue(G_Before.gt(toBN("0"))); - - // Get front ends' snapshots before - for (frontEnd of [frontEnd_1, frontEnd_2, frontEnd_3]) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); - - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends, since S corresponds to ETH gain) - assert.equal(snapshot[1], dec(1, 18)); // P - assert.equal(snapshot[2], "0"); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } - - // --- TEST --- - - // Check A, B, C have non-zero ETH gain - assert.isTrue((await stabilityPool.getDepositorETHGain(A)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(B)).gt(ZERO)); - assert.isTrue((await stabilityPool.getDepositorETHGain(C)).gt(ZERO)); + assert.isFalse(await sortedTroves.contains(defaulter_1)); await priceFeed.setPrice(dec(200, 18)); - // A, B, C withdraw ETH gain to troves. Grab G at each stage, as it can increase a bit - // between topups, because some block.timestamp time passes (and LQTY is issued) between ops - const G1 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.withdrawETHGainToTrove(A, A, { from: A }); - - const G2 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch - ); - await stabilityPool.withdrawETHGainToTrove(B, B, { from: B }); - - const G3 = await stabilityPool.epochToScaleToG( - currentScale, - currentEpoch + // D attempts to withdraw his ETH gain to Trove + await th.assertRevert( + stabilityPool.withdrawETHGainToTrove(dennis, dennis, { from: dennis }), + "caller must have an active trove to withdraw ETHGain to" ); - await stabilityPool.withdrawETHGainToTrove(C, C, { from: C }); - - const frontEnds = [frontEnd_1, frontEnd_2, frontEnd_3]; - const G_Values = [G1, G2, G3]; - - // Map frontEnds to the value of G at time the deposit was made - frontEndToG = th.zipToObject(frontEnds, G_Values); - - // Get front ends' snapshots after - for (const [frontEnd, G] of Object.entries(frontEndToG)) { - const snapshot = await stabilityPool.frontEndSnapshots(frontEnd); - - // Check snapshots are the expected values - assert.equal(snapshot[0], "0"); // S (should always be 0 for front ends) - assert.isTrue(snapshot[1].eq(P_Before)); // P - assert.isTrue(snapshot[2].eq(G)); // G - assert.equal(snapshot[3], "0"); // scale - assert.equal(snapshot[4], "0"); // epoch - } }); it("withdrawETHGainToTrove(): reverts when depositor has no ETH gain", async () => { await openTrove({ - extraLUSDAmount: toBN(dec(100000, 18)), + extraBoldAmount: toBN(dec(100000, 18)), ICR: toBN(dec(10, 18)), extraParams: { from: whale }, }); - // Whale transfers LUSD to A, B + // Whale transfers Bold to A, B await boldToken.transfer(A, dec(10000, 18), { from: whale }); await boldToken.transfer(B, dec(20000, 18), { from: whale }); // C, D open troves await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: C }, }); await openTrove({ - extraLUSDAmount: toBN(dec(4000, 18)), + extraBoldAmount: toBN(dec(4000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: D }, }); // A, B, C, D provide to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: A }); - await stabilityPool.provideToSP(dec(20, 18), ZERO_ADDRESS, { from: B }); - await stabilityPool.provideToSP(dec(30, 18), frontEnd_2, { from: C }); - await stabilityPool.provideToSP(dec(40, 18), ZERO_ADDRESS, { from: D }); + await stabilityPool.provideToSP(dec(10, 18), { from: A }); + await stabilityPool.provideToSP(dec(20, 18), { from: B }); + await stabilityPool.provideToSP(dec(30, 18), { from: C }); + await stabilityPool.provideToSP(dec(40, 18), { from: D }); - // fastforward time, and E makes a deposit, creating LQTY rewards for all + // fastforward time, and E makes a deposit await th.fastForwardTime( timeValues.SECONDS_IN_ONE_HOUR, web3.currentProvider ); await openTrove({ - extraLUSDAmount: toBN(dec(3000, 18)), + extraBoldAmount: toBN(dec(3000, 18)), ICR: toBN(dec(2, 18)), extraParams: { from: E }, }); - await stabilityPool.provideToSP(dec(3000, 18), ZERO_ADDRESS, { from: E }); + await stabilityPool.provideToSP(dec(3000, 18), { from: E }); // Confirm A, B, C have zero ETH gain assert.equal(await stabilityPool.getDepositorETHGain(A), "0"); @@ -6046,142 +3755,6 @@ contract("StabilityPool", async (accounts) => { await th.assertRevert(txPromise_C); await th.assertRevert(txPromise_D); }); - - it("registerFrontEnd(): registers the front end and chosen kickback rate", async () => { - const unregisteredFrontEnds = [A, B, C, D, E]; - - for (const frontEnd of unregisteredFrontEnds) { - assert.isFalse((await stabilityPool.frontEnds(frontEnd))[1]); // check inactive - assert.equal((await stabilityPool.frontEnds(frontEnd))[0], "0"); // check no chosen kickback rate - } - - await stabilityPool.registerFrontEnd(dec(1, 18), { from: A }); - await stabilityPool.registerFrontEnd("897789897897897", { from: B }); - await stabilityPool.registerFrontEnd("99990098", { from: C }); - await stabilityPool.registerFrontEnd("37", { from: D }); - await stabilityPool.registerFrontEnd("0", { from: E }); - - // Check front ends are registered as active, and have correct kickback rates - assert.isTrue((await stabilityPool.frontEnds(A))[1]); - assert.equal((await stabilityPool.frontEnds(A))[0], dec(1, 18)); - - assert.isTrue((await stabilityPool.frontEnds(B))[1]); - assert.equal((await stabilityPool.frontEnds(B))[0], "897789897897897"); - - assert.isTrue((await stabilityPool.frontEnds(C))[1]); - assert.equal((await stabilityPool.frontEnds(C))[0], "99990098"); - - assert.isTrue((await stabilityPool.frontEnds(D))[1]); - assert.equal((await stabilityPool.frontEnds(D))[0], "37"); - - assert.isTrue((await stabilityPool.frontEnds(E))[1]); - assert.equal((await stabilityPool.frontEnds(E))[0], "0"); - }); - - it("registerFrontEnd(): reverts if the front end is already registered", async () => { - await stabilityPool.registerFrontEnd(dec(1, 18), { from: A }); - await stabilityPool.registerFrontEnd("897789897897897", { from: B }); - await stabilityPool.registerFrontEnd("99990098", { from: C }); - - const _2ndAttempt_A = stabilityPool.registerFrontEnd(dec(1, 18), { - from: A, - }); - const _2ndAttempt_B = stabilityPool.registerFrontEnd("897789897897897", { - from: B, - }); - const _2ndAttempt_C = stabilityPool.registerFrontEnd("99990098", { - from: C, - }); - - await th.assertRevert( - _2ndAttempt_A, - "StabilityPool: must not already be a registered front end" - ); - await th.assertRevert( - _2ndAttempt_B, - "StabilityPool: must not already be a registered front end" - ); - await th.assertRevert( - _2ndAttempt_C, - "StabilityPool: must not already be a registered front end" - ); - }); - - it("registerFrontEnd(): reverts if the kickback rate >1", async () => { - const invalidKickbackTx_A = stabilityPool.registerFrontEnd(dec(1, 19), { - from: A, - }); - const invalidKickbackTx_B = stabilityPool.registerFrontEnd( - "1000000000000000001", - { from: A } - ); - const invalidKickbackTx_C = stabilityPool.registerFrontEnd( - dec(23423, 45), - { from: A } - ); - const invalidKickbackTx_D = stabilityPool.registerFrontEnd(maxBytes32, { - from: A, - }); - - await th.assertRevert( - invalidKickbackTx_A, - "StabilityPool: Kickback rate must be in range [0,1]" - ); - await th.assertRevert( - invalidKickbackTx_B, - "StabilityPool: Kickback rate must be in range [0,1]" - ); - await th.assertRevert( - invalidKickbackTx_C, - "StabilityPool: Kickback rate must be in range [0,1]" - ); - await th.assertRevert( - invalidKickbackTx_D, - "StabilityPool: Kickback rate must be in range [0,1]" - ); - }); - - it("registerFrontEnd(): reverts if address has a non-zero deposit already", async () => { - // C, D, E open troves - await openTrove({ - extraLUSDAmount: toBN(dec(10, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: C }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(10, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: D }, - }); - await openTrove({ - extraLUSDAmount: toBN(dec(10, 18)), - ICR: toBN(dec(2, 18)), - extraParams: { from: E }, - }); - - // C, E provides to SP - await stabilityPool.provideToSP(dec(10, 18), frontEnd_1, { from: C }); - await stabilityPool.provideToSP(dec(10, 18), ZERO_ADDRESS, { from: E }); - - const txPromise_C = stabilityPool.registerFrontEnd(dec(1, 18), { - from: C, - }); - const txPromise_E = stabilityPool.registerFrontEnd(dec(1, 18), { - from: E, - }); - await th.assertRevert( - txPromise_C, - "StabilityPool: User must have no deposit" - ); - await th.assertRevert( - txPromise_E, - "StabilityPool: User must have no deposit" - ); - - // D, with no deposit, successfully registers a front end - const txD = await stabilityPool.registerFrontEnd(dec(1, 18), { from: D }); - assert.isTrue(txD.receipt.status); - }); }); });