From 03f323ec96a89c13beaf8d5ca013e9bd3a54e328 Mon Sep 17 00:00:00 2001 From: Pierre Bertet Date: Wed, 3 Apr 2024 15:55:25 +0100 Subject: [PATCH] =?UTF-8?q?TroveManagerTest:=20make=20use=20of=20hardhat?= =?UTF-8?q?=E2=80=99s=20loadFixture()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a test of migrating to loadFixture() using TroveManagerTest only for now. The migration consists of converting beforeEach() into a fixture, which is recommended by Hardhat mostly for performance reasons [1]. On my computer, this test went from ~54s to ~18s (~67% faster). Before: pnpm test test/TroveManagerTest.js 53.90s user 6.48s system 145% cpu 41.495 total After: pnpm test test/TroveManagerTest.js 17.98s user 1.81s system 138% cpu 14.309 total [1] https://hardhat.org/hardhat-runner/docs/guides/test-contracts#using-fixtures --- contracts/test/TroveManagerTest.js | 306 ++++++++++++++++++++++++----- 1 file changed, 254 insertions(+), 52 deletions(-) diff --git a/contracts/test/TroveManagerTest.js b/contracts/test/TroveManagerTest.js index 2a6ab2e3..a83b9438 100644 --- a/contracts/test/TroveManagerTest.js +++ b/contracts/test/TroveManagerTest.js @@ -3,6 +3,7 @@ const testHelpers = require("../utils/testHelpers.js"); const { fundAccounts } = require("../utils/fundAccounts.js"); const TroveManagerTester = artifacts.require("./TroveManagerTester.sol"); const BoldToken = artifacts.require("./BoldToken.sol"); +const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); const th = testHelpers.TestHelper; const dec = th.dec; @@ -47,51 +48,14 @@ contract("TroveManager", async (accounts) => { E, ] = accounts; - const [bountyAddress, lpRewardsAddress, multisig] = accounts.slice(997, 1000); - - let priceFeed; - let boldToken; - let sortedTroves; - let troveManager; - let activePool; - let stabilityPool; - let collSurplusPool; - let defaultPool; - let borrowerOperations; - let hintHelpers; - - let contracts; - - const getOpenTroveTotalDebt = async (boldAmount) => - th.getOpenTroveTotalDebt(contracts, boldAmount); - const getOpenTroveBoldAmount = async (totalDebt) => - th.getOpenTroveBoldAmount(contracts, totalDebt); - const getActualDebtFromComposite = async (compositeDebt) => - th.getActualDebtFromComposite(compositeDebt, contracts); - const getNetBorrowingAmount = async (debtWithFee) => - th.getNetBorrowingAmount(contracts, debtWithFee); - const openTrove = async (params) => th.openTrove(contracts, params); - const withdrawBold = async (params) => th.withdrawBold(contracts, params); - - beforeEach(async () => { - contracts = await deploymentHelper.deployLiquityCore(); + async function deployContractsAndFundFixture() { + const contracts = await deploymentHelper.deployLiquityCore(); contracts.troveManager = await TroveManagerTester.new(); contracts.boldToken = await BoldToken.new( contracts.troveManager.address, contracts.stabilityPool.address, - contracts.borrowerOperations.address - ) - - priceFeed = contracts.priceFeedTestnet; - boldToken = contracts.boldToken; - sortedTroves = contracts.sortedTroves; - troveManager = contracts.troveManager; - activePool = contracts.activePool; - stabilityPool = contracts.stabilityPool; - defaultPool = contracts.defaultPool; - collSurplusPool = contracts.collSurplusPool; - borrowerOperations = contracts.borrowerOperations; - hintHelpers = contracts.hintHelpers; + contracts.borrowerOperations.address, + ); await deploymentHelper.connectCoreContracts(contracts); await fundAccounts([ @@ -116,9 +80,28 @@ contract("TroveManager", async (accounts) => { D, E, ], contracts.WETH); - }); + + return { + getOpenTroveBoldAmount: async (totalDebt) => th.getOpenTroveBoldAmount(contracts, totalDebt), + getNetBorrowingAmount: async (debtWithFee) => th.getNetBorrowingAmount(contracts, debtWithFee), + openTrove: async (params) => th.openTrove(contracts, params), + withdrawBold: async (params) => th.withdrawBold(contracts, params), + contracts: { + ...contracts, + priceFeed: contracts.priceFeedTestnet, + }, + }; + } it("liquidate(): closes a Trove that has ICR < MCR", async () => { + const { + getNetBorrowingAmount, + openTrove, + withdrawBold, + contracts, + } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale } }); await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: alice } }); @@ -155,6 +138,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): decreases ActivePool ETH and BoldDebt by correct amounts", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, activePool } = contracts; + // --- SETUP --- const { collateral: A_collateral, totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: alice } }); @@ -204,6 +190,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): increases DefaultPool ETH and Bold debt by correct amounts", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, defaultPool } = contracts; + // --- SETUP --- const { collateral: A_collateral, totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: alice } }); @@ -250,6 +239,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): removes the Trove's stake from the total stakes", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager } = contracts; + // --- SETUP --- const { collateral: A_collateral, totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: alice } }); @@ -277,6 +269,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): Removes the correct trove from the TroveOwners array, and moves the last array element to the new empty slot", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + // --- SETUP --- await openTrove({ ICR: toBN(dec(10, 18)), extraParams: { from: whale } }); @@ -342,6 +337,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): updates the snapshots of total stakes and total collateral", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager } = contracts; + // --- SETUP --- const { collateral: A_collateral, totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: alice } }); @@ -389,6 +387,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): updates the L_ETH and L_boldDebt reward-per-unit-staked totals", async () => { + const { contracts, openTrove, withdrawBold } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + // --- SETUP --- const { collateral: A_collateral, totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(8, 18)), extraParams: { from: alice } }); @@ -489,6 +490,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): Liquidates undercollateralized trove if there are two troves in the system", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + await openTrove({ ICR: toBN(dec(200, 18)), extraParams: { from: bob, value: dec(100, "ether") }, @@ -534,6 +538,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): reverts if trove is non-existent", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, sortedTroves } = contracts; + await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: alice } }); await openTrove({ ICR: toBN(dec(21, 17)), extraParams: { from: bob } }); @@ -555,6 +562,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): reverts if trove has been closed", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, sortedTroves, priceFeed } = contracts; + await openTrove({ ICR: toBN(dec(8, 18)), extraParams: { from: alice } }); await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: bob } }); await openTrove({ ICR: toBN(dec(2, 18)), extraParams: { from: carol } }); @@ -586,6 +596,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): does nothing if trove has >= 110% ICR", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, sortedTroves, priceFeed } = contracts; + await openTrove({ ICR: toBN(dec(3, 18)), extraParams: { from: whale } }); await openTrove({ ICR: toBN(dec(3, 18)), extraParams: { from: bob } }); @@ -619,6 +632,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): Given the same price and no other trove changes, complete Pool offsets restore the TCR to its value prior to the defaulters opening troves", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // Whale provides Bold to SP const spDeposit = toBN(dec(100, 24)); await openTrove({ @@ -684,6 +700,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): Pool offsets increase the TCR", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // Whale provides Bold to SP const spDeposit = toBN(dec(100, 24)); await openTrove({ @@ -750,6 +769,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): a pure redistribution reduces the TCR only as a result of compensation", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + await openTrove({ ICR: toBN(dec(4, 18)), extraParams: { from: whale } }); await openTrove({ ICR: toBN(dec(10, 18)), extraParams: { from: alice } }); @@ -861,6 +883,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): does not affect the SP deposit or ETH gain when called on an SP depositor's address that has no trove", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, boldToken, stabilityPool } = contracts; + await openTrove({ ICR: toBN(dec(10, 18)), extraParams: { from: whale } }); const spDeposit = toBN(dec(1, 24)); await openTrove({ @@ -927,6 +952,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): does not liquidate a SP depositor's trove with ICR > 110%, and does not affect their SP deposit or ETH gain", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + await openTrove({ ICR: toBN(dec(10, 18)), extraParams: { from: whale } }); const spDeposit = toBN(dec(1, 24)); await openTrove({ @@ -992,6 +1020,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): liquidates a SP depositor's trove with ICR < 110%, and the liquidation correctly impacts their SP deposit and ETH gain", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + const A_spDeposit = toBN(dec(3, 24)); const B_spDeposit = toBN(dec(1, 24)); await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale } }); @@ -1099,14 +1130,17 @@ contract("TroveManager", async (accounts) => { th .applyLiquidationFee(B_collateral) .mul(bob_Deposit_Before) - .div(totalDeposits) - ) + .div(totalDeposits), + ), ), - 1000000 + 1000000, ); }); it("liquidate(): does not alter the liquidated user's token balance", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { activePool, priceFeed, troveManager, sortedTroves, boldToken, defaultPool } = contracts; + await openTrove({ ICR: toBN(dec(10, 18)), extraParams: { from: whale } }); const { boldAmount: A_boldAmount } = await openTrove({ ICR: toBN(dec(2, 18)), @@ -1161,6 +1195,9 @@ contract("TroveManager", async (accounts) => { }); it("liquidate(): liquidates based on entire/collateral debt (including pending rewards), not raw collateral/debt", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + await openTrove({ ICR: toBN(dec(8, 18)), extraBoldAmount: toBN(dec(100, 18)), @@ -1263,6 +1300,9 @@ contract("TroveManager", async (accounts) => { // --- batchLiquidateTroves() --- it("batchLiquidateTroves(): liquidates based on entire/collateral debt (including pending rewards), not raw collateral/debt", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + await openTrove({ ICR: toBN(dec(400, 16)), extraParams: { from: alice } }); await openTrove({ ICR: toBN(dec(221, 16)), extraParams: { from: bob } }); await openTrove({ ICR: toBN(dec(200, 16)), extraParams: { from: carol } }); @@ -1339,6 +1379,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): liquidates troves with ICR < MCR", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale } }); // A, B, C open troves that will remain active when price drops to 100 @@ -1397,6 +1440,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): does not affect the liquidated user's token balances", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, boldToken } = contracts; + await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale } }); // D, E, F open troves that will fall below MCR when price drops to 100 @@ -1435,13 +1481,16 @@ contract("TroveManager", async (accounts) => { // Check token balances of users whose troves were liquidated, have not changed assert.equal( (await boldToken.balanceOf(dennis)).toString(), - D_balanceBefore + D_balanceBefore, ); assert.equal((await boldToken.balanceOf(erin)).toString(), E_balanceBefore); assert.equal((await boldToken.balanceOf(flyn)).toString(), F_balanceBefore); }); it("batchLiquidateTroves(): A liquidation sequence containing Pool offsets increases the TCR", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // Whale provides 500 Bold to SP await openTrove({ ICR: toBN(dec(100, 18)), @@ -1516,6 +1565,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): A liquidation sequence of pure redistributions decreases the TCR, due to gas compensation, but up to 0.5%", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + const { collateral: W_coll, totalDebt: W_debt } = await openTrove({ ICR: toBN(dec(100, 18)), extraParams: { from: whale }, @@ -1613,15 +1665,18 @@ contract("TroveManager", async (accounts) => { totalCollNonDefaulters .add(th.applyLiquidationFee(totalCollDefaulters)) .mul(price) - .div(totalDebt) + .div(totalDebt), ), - 1000 + 1000, ); assert.isTrue(TCR_Before.gte(TCR_After)); assert.isTrue(TCR_After.gte(TCR_Before.mul(toBN(995)).div(toBN(1000)))); }); it("batchLiquidateTroves(): Liquidating troves with SP deposits correctly impacts their SP deposit and ETH gain", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // Whale provides 400 Bold to the SP const whaleDeposit = toBN(dec(40000, 18)); await openTrove({ @@ -1791,6 +1846,9 @@ contract("TroveManager", async (accounts) => { // Can we achieve / test the same thing using another liquidation function? it("batchLiquidateTroves(): liquidates a Trove that a) was skipped in a previous liquidation and b) has pending rewards", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool, defaultPool, borrowerOperations } = contracts; + // A, B, C, D, E open troves await openTrove({ ICR: toBN(dec(300, 16)), extraParams: { from: C } }); await openTrove({ ICR: toBN(dec(364, 16)), extraParams: { from: D } }); @@ -1876,6 +1934,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): closes every trove with ICR < MCR in the given array", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // --- SETUP --- await openTrove({ ICR: toBN(dec(100, 18)), extraParams: { from: whale } }); @@ -1939,6 +2000,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): does not liquidate troves that are not in the given array", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // --- SETUP --- await openTrove({ ICR: toBN(dec(100, 18)), extraParams: { from: whale } }); @@ -2008,6 +2072,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): does not close troves with ICR >= MCR in the given array", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // --- SETUP --- await openTrove({ ICR: toBN(dec(100, 18)), extraParams: { from: whale } }); @@ -2071,6 +2138,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): reverts if array is empty", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // --- SETUP --- await openTrove({ ICR: toBN(dec(100, 18)), extraParams: { from: whale } }); @@ -2107,12 +2177,15 @@ contract("TroveManager", async (accounts) => { } catch (error) { assert.include( error.message, - "TroveManager: Calldata address array must not be empty" + "TroveManager: Calldata address array must not be empty", ); } }); it("batchLiquidateTroves(): skips if trove is non-existent", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool } = contracts; + // --- SETUP --- const spDeposit = toBN(dec(500000, 18)); await openTrove({ @@ -2197,6 +2270,9 @@ contract("TroveManager", async (accounts) => { }); it("batchLiquidateTroves(): skips if a trove has been closed", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves, stabilityPool, boldToken, borrowerOperations } = contracts; + // --- SETUP --- const spDeposit = toBN(dec(500000, 18)); await openTrove({ @@ -2296,6 +2372,8 @@ contract("TroveManager", async (accounts) => { // Many of these tests rely on specific ICR ordering in the setup, and close fully redeemed. // It may be more efficient to write wholly new redemption tests in Solidity for Foundry. it.skip("getRedemptionHints(): gets the address of the first Trove and the final ICR of the last Trove involved in a redemption", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + // --- SETUP --- const partialRedemptionAmount = toBN(dec(100, 18)); const { collateral: A_coll, totalDebt: A_totalDebt } = await openTrove({ @@ -2331,6 +2409,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("getRedemptionHints(): returns 0 as partialRedemptionHintNICR when reaching _maxIterations", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + // --- SETUP --- await openTrove({ ICR: toBN(dec(310, 16)), extraParams: { from: alice } }); await openTrove({ ICR: toBN(dec(290, 16)), extraParams: { from: bob } }); @@ -2346,13 +2426,16 @@ contract("TroveManager", async (accounts) => { const { partialRedemptionHintNICR } = await hintHelpers.getRedemptionHints( "210" + _18_zeros, price, - 2 + 2, ); // limit _maxIterations to 2 assert.equal(partialRedemptionHintNICR, "0"); }); it.skip("redeemCollateral(): cancels the provided Bold with debt from Troves with the lowest ICRs and sends an equivalent amount of Ether", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, boldToken, priceFeed, sortedTroves } = contracts; + // --- SETUP --- const { totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(310, 16)), @@ -2471,6 +2554,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): with invalid first hint, zero address", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, priceFeed, sortedTroves } = contracts; + // --- SETUP --- const { totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(310, 16)), @@ -2580,6 +2666,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): with invalid first hint, non-existent trove", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + // --- SETUP --- const { totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(310, 16)), @@ -2689,6 +2778,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): with invalid first hint, trove below MCR", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, troveManager, sortedTroves } = contracts; + // --- SETUP --- const { totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(310, 16)), @@ -2803,6 +2895,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): ends the redemption sequence when the token redemption request has been filled", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + // --- SETUP --- await openTrove({ ICR: toBN(dec(100, 18)), extraParams: { from: whale } }); @@ -2900,6 +2994,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): ends the redemption sequence when max iterations have been reached", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + // --- SETUP --- await openTrove({ ICR: toBN(dec(100, 18)), extraParams: { from: whale } }); @@ -2975,6 +3071,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): performs partial redemption if resultant debt is > minimum net debt", async () => { + const { contracts, getOpenTroveBoldAmount } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, boldToken, sortedTroves } = contracts; + await th.openTroveWrapper(contracts, th._100pct, await getOpenTroveBoldAmount(dec(10000, 18)), @@ -3029,6 +3128,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): doesn't perform partial redemption if resultant debt would be < minimum net debt", async () => { + const { contracts, getOpenTroveBoldAmount } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, boldToken, sortedTroves } = contracts; + await th.openTroveWrapper(contracts, th._100pct, await getOpenTroveBoldAmount(dec(6000, 18)), @@ -3084,6 +3186,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): doesnt perform the final partial redemption in the sequence if the hint is out-of-date", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, priceFeed, sortedTroves } = contracts; + // --- SETUP --- const { totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(363, 16)), @@ -3214,6 +3319,9 @@ contract("TroveManager", async (accounts) => { // active debt cannot be zero, as there’s a positive min debt enforced, and at least a trove must exist it.skip("redeemCollateral(): can redeem if there is zero active debt but non-zero debt in DefaultPool", async () => { + const { contracts, getOpenTroveBoldAmount, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, boldToken, priceFeed } = contracts; + // --- SETUP --- const amount = await getOpenTroveBoldAmount(dec(110, 18)); @@ -3273,6 +3381,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): doesn't touch Troves with ICR < 110%", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + // --- SETUP --- const { netDebt: A_debt } = await openTrove({ @@ -3321,6 +3431,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): finds the last Trove with ICR == 110% even if there is more than one", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + // --- SETUP --- const amount1 = toBN(dec(100, 18)); const { totalDebt: A_totalDebt } = await openTrove({ @@ -3397,6 +3509,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): reverts when TCR < MCR", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed } = contracts; + await openTrove({ ICR: toBN(dec(200, 16)), extraParams: { from: alice } }); await openTrove({ ICR: toBN(dec(200, 16)), extraParams: { from: bob } }); await openTrove({ ICR: toBN(dec(200, 16)), extraParams: { from: carol } }); @@ -3423,6 +3538,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): reverts when argument _amount is 0", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale } }); // Alice opens trove and transfers 500Bold to Erin, the would-be redeemer @@ -3462,6 +3579,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): reverts if max fee > 100%", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + await openTrove({ ICR: toBN(dec(400, 16)), extraBoldAmount: dec(10, 18), @@ -3512,6 +3631,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): reverts if max fee < 0.5%", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + await openTrove({ ICR: toBN(dec(400, 16)), extraBoldAmount: dec(10, 18), @@ -3572,6 +3693,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): reverts if fee exceeds max fee percentage", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { boldToken, troveManager } = contracts; + const { totalDebt: A_totalDebt } = await openTrove({ ICR: toBN(dec(400, 16)), extraBoldAmount: dec(80, 18), @@ -3657,6 +3781,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): doesn't affect the Stability Pool deposits or ETH gain of redeemed-from troves", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, boldToken, stabilityPool, priceFeed, sortedTroves } = contracts; + await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale } }); // B, C, D, F open trove @@ -3794,6 +3921,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): caller can redeem their entire BoldToken balance", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + const { collateral: W_coll, totalDebt: W_totalDebt } = await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale }, @@ -3894,6 +4023,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): reverts when requested redemption amount exceeds caller's Bold token balance", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + const { collateral: W_coll, totalDebt: W_totalDebt } = await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale }, @@ -4090,19 +4221,22 @@ contract("TroveManager", async (accounts) => { partialRedemptionHintNICR, 0, th._100pct, - { from: erin } + { from: erin }, ); assert.isFalse(redemptionTx.receipt.status); } catch (error) { assert.include(error.message, "revert"); assert.include( error.message, - "Requested redemption amount must be <= user's Bold token balance" + "Requested redemption amount must be <= user's Bold token balance", ); } }); it.skip("redeemCollateral(): value of issued ETH == face value of redeemed Bold (assuming 1 Bold has value of $1)", async () => { + const { openTrove, contracts } = await loadFixture(deployContractsAndFundFixture); + const { boldToken, troveManager, priceFeed, activePool, sortedTroves } = contracts; + const { collateral: W_coll } = await openTrove({ ICR: toBN(dec(20, 18)), extraParams: { from: whale }, @@ -4260,6 +4394,9 @@ contract("TroveManager", async (accounts) => { // it doesn’t make much sense as there’s now min debt enforced and at least one trove must remain active // the only way to test it is before any trove is opened it.skip("redeemCollateral(): reverts if there is zero outstanding system debt", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, boldToken } = contracts; + // --- SETUP --- illegally mint Bold to Bob await boldToken.unprotectedMint(bob, dec(100, 18)); @@ -4300,6 +4437,8 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): reverts if caller's tries to redeem more than the outstanding system debt", async () => { + const { openTrove } = await loadFixture(deployContractsAndFundFixture); + // --- SETUP --- illegally mint Bold to Bob await boldToken.unprotectedMint(bob, "101000000000000000000"); @@ -4360,6 +4499,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): a redemption sends the ETH remainder (ETHDrawn - gas) to the redeemer", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed, activePool, defaultPool } = contracts; + // time fast-forwards 1 year, and multisig stakes 1 LQTY await th.fastForwardTime( timeValues.SECONDS_IN_ONE_YEAR, @@ -4591,6 +4733,9 @@ contract("TroveManager", async (accounts) => { }; it.skip("redeemCollateral(): emits correct debt and coll values in each redeemed trove's TroveUpdated event", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { sortedTroves } = contracts; + const { netDebt: W_netDebt } = await openTrove({ ICR: toBN(dec(20, 18)), extraBoldAmount: dec(10000, 18), @@ -4682,6 +4827,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): a redemption that closes a trove leaves the trove's ETH surplus (collateral - ETH drawn) available for the trove owner to claim", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { collSurplusPool } = contracts; + const { A_netDebt, A_coll, B_netDebt, B_coll, C_netDebt, C_coll } = await redeemCollateral3Full1Partial(); @@ -4716,6 +4864,9 @@ contract("TroveManager", async (accounts) => { }); it.skip("redeemCollateral(): a redemption that closes a trove leaves the trove's ETH surplus (collateral - ETH drawn) available for the trove owner after re-opening trove", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed } = contracts; + const { A_netDebt, A_coll: A_collBefore, @@ -4777,6 +4928,9 @@ contract("TroveManager", async (accounts) => { }); it("getPendingBoldDebtReward(): Returns 0 if there is no pending BoldDebt reward", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, stabilityPool, priceFeed, sortedTroves } = contracts; + // Make some troves const { totalDebt } = await openTrove({ ICR: toBN(dec(2, 18)), @@ -4820,6 +4974,9 @@ contract("TroveManager", async (accounts) => { }); it("getPendingETHReward(): Returns 0 if there is no pending ETH reward", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, stabilityPool, priceFeed, sortedTroves } = contracts; + // make some troves const { totalDebt } = await openTrove({ ICR: toBN(dec(2, 18)), @@ -4864,6 +5021,9 @@ contract("TroveManager", async (accounts) => { // --- computeICR --- it("computeICR(): Returns 0 if trove's coll is worth 0", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const price = 0; const coll = dec(1, "ether"); const debt = dec(100, 18); @@ -4874,6 +5034,9 @@ contract("TroveManager", async (accounts) => { }); it("computeICR(): Returns 2^256-1 for ETH:USD = 100, coll = 1 ETH, debt = 100 Bold", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const price = dec(100, 18); const coll = dec(1, "ether"); const debt = dec(100, 18); @@ -4884,6 +5047,9 @@ contract("TroveManager", async (accounts) => { }); it("computeICR(): returns correct ICR for ETH:USD = 100, coll = 200 ETH, debt = 30 Bold", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const price = dec(100, 18); const coll = dec(200, "ether"); const debt = dec(30, 18); @@ -4894,6 +5060,9 @@ contract("TroveManager", async (accounts) => { }); it("computeICR(): returns correct ICR for ETH:USD = 250, coll = 1350 ETH, debt = 127 Bold", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const price = "250000000000000000000"; const coll = "1350000000000000000000"; const debt = "127000000000000000000"; @@ -4904,6 +5073,9 @@ contract("TroveManager", async (accounts) => { }); it("computeICR(): returns correct ICR for ETH:USD = 100, coll = 1 ETH, debt = 54321 Bold", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const price = dec(100, 18); const coll = dec(1, "ether"); const debt = "54321000000000000000000"; @@ -4914,6 +5086,9 @@ contract("TroveManager", async (accounts) => { }); it("computeICR(): Returns 2^256-1 if trove has non-zero coll and zero debt", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const price = dec(100, 18); const coll = dec(1, "ether"); const debt = 0; @@ -4931,6 +5106,9 @@ contract("TroveManager", async (accounts) => { //TCR < 150% it("checkRecoveryMode(): Returns true when TCR < 150%", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed } = contracts; + await priceFeed.setPrice(dec(100, 18)); await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: alice } }); @@ -4947,6 +5125,9 @@ contract("TroveManager", async (accounts) => { // TCR == 150% it("checkRecoveryMode(): Returns false when TCR == 150%", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed } = contracts; + await priceFeed.setPrice(dec(100, 18)); await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: alice } }); @@ -4961,6 +5142,9 @@ contract("TroveManager", async (accounts) => { // > 150% it("checkRecoveryMode(): Returns false when TCR > 150%", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed } = contracts; + await priceFeed.setPrice(dec(100, 18)); await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: alice } }); @@ -4977,6 +5161,9 @@ contract("TroveManager", async (accounts) => { // check 0 it("checkRecoveryMode(): Returns false when TCR == 0", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { priceFeed } = contracts; + await priceFeed.setPrice(dec(100, 18)); await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: alice } }); @@ -4994,6 +5181,9 @@ contract("TroveManager", async (accounts) => { // --- Getters --- it("getTroveStake(): Returns stake", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const { collateral: A_coll } = await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: A }, @@ -5011,6 +5201,9 @@ contract("TroveManager", async (accounts) => { }); it("getTroveColl(): Returns coll", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const { collateral: A_coll } = await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: A }, @@ -5025,6 +5218,9 @@ contract("TroveManager", async (accounts) => { }); it("getTroveDebt(): Returns debt", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + const { totalDebt: totalDebtA } = await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: A }, @@ -5044,6 +5240,9 @@ contract("TroveManager", async (accounts) => { }); it("getTroveStatus(): Returns status", async () => { + const { contracts, openTrove } = await loadFixture(deployContractsAndFundFixture); + const { troveManager, boldToken, borrowerOperations } = contracts; + const { totalDebt: B_totalDebt } = await openTrove({ ICR: toBN(dec(150, 16)), extraParams: { from: B }, @@ -5068,6 +5267,9 @@ contract("TroveManager", async (accounts) => { }); it("hasPendingRewards(): Returns false it trove is not active", async () => { + const { contracts } = await loadFixture(deployContractsAndFundFixture); + const { troveManager } = contracts; + assert.isFalse(await troveManager.hasPendingRewards(alice)); }); });