diff --git a/contracts/src/Dependencies/LiquityMath.sol b/contracts/src/Dependencies/LiquityMath.sol index 69adf7776..0acf18fd6 100644 --- a/contracts/src/Dependencies/LiquityMath.sol +++ b/contracts/src/Dependencies/LiquityMath.sol @@ -101,7 +101,7 @@ library LiquityMath { return newCollRatio; } - // Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR. + // Return the maximal value for uint256 if the debt is 0. Represents "infinite" CR. else { // if (_debt == 0) return 2**256 - 1; } diff --git a/contracts/src/test/TestContracts/BaseTest.sol b/contracts/src/test/TestContracts/BaseTest.sol index e0ed1c29f..e6a48df2e 100644 --- a/contracts/src/test/TestContracts/BaseTest.sol +++ b/contracts/src/test/TestContracts/BaseTest.sol @@ -47,6 +47,27 @@ contract BaseTest is Test { GasPool gasPool; IInterestRouter mockInterestRouter; + // Structs for use in test where we need to bi-pass "stack-too-deep" errors + struct TroveDebtRequests { + uint256 A; + uint256 B; + uint256 C; + } + + struct TroveCollAmounts { + uint256 A; + uint256 B; + uint256 C; + } + + struct TroveInterestRates { + uint256 A; + uint256 B; + uint256 C; + } + + // --- functions --- + function createAccounts() public { address[10] memory tempAccounts; for (uint256 i = 0; i < accounts.getAccountsCount(); i++) { diff --git a/contracts/src/test/interestRateAggregate.t.sol b/contracts/src/test/interestRateAggregate.t.sol index b6eb0256a..bfd17d052 100644 --- a/contracts/src/test/interestRateAggregate.t.sol +++ b/contracts/src/test/interestRateAggregate.t.sol @@ -80,7 +80,7 @@ contract InterestRateAggregate is DevTestSetup { // --- calcTroveAccruedInterest // returns 0 for non-existent trove - function testCalcPendingTroveInterestReturns0When0AggRecordedDebt() public { + function testCalcTroveAccruedInterestReturns0When0AggRecordedDebt() public { priceFeed.setPrice(2000e18); assertEq(troveManager.calcTroveAccruedInterest(A), 0); @@ -99,7 +99,7 @@ contract InterestRateAggregate is DevTestSetup { } // returns 0 for 0 time passed - function testCalcPendingTroveInterestReturns0For0TimePassed() public { + function testCalcTroveAccruedInterestReturns0For0TimePassed() public { priceFeed.setPrice(2000e18); openTroveNoHints100pctMaxFee(A, 2 ether, 2000e18, 25e16); @@ -111,7 +111,7 @@ contract InterestRateAggregate is DevTestSetup { assertEq(troveManager.calcTroveAccruedInterest(B), 0); } - function testCalcPendingTroveInterestReturns0For0InterestRate() public { + function testCalcTroveAccruedInterestReturns0For0InterestRate() public { priceFeed.setPrice(2000e18); openTroveNoHints100pctMaxFee(A, 2 ether, 2000e18, 0); @@ -124,7 +124,7 @@ contract InterestRateAggregate is DevTestSetup { } // TODO: create additional corresponding fuzz test - function testCalcPendingTroveInterestReturnsCorrectInterestForGivenPeriod() public { + function testCalcTroveAccruedInterestReturnsCorrectInterestForGivenPeriod() public { priceFeed.setPrice(2000e18); uint256 annualRate_A = 1e18; @@ -955,9 +955,6 @@ contract InterestRateAggregate is DevTestSetup { assertEq(activePool.getRecordedDebtSum(), recordedDebtSum_1 + pendingInterest); } - // TODO: getEntireDebt and getTCR basic tests - - // --- withdrawBold tests --- function testWithdrawBoldWithNoPendingRewardIncreasesAggRecordedDebtByPendingAggInterestPlusBorrowerDebtChange() public { @@ -1872,12 +1869,6 @@ contract InterestRateAggregate is DevTestSetup { assertEq(activePool.getRecordedDebtSum(), recordedDebt_1 - oldTroveRecordedDebt + newTroveRecordedDebt); } - // TODO: mixed collateral & debt adjustment opps - // TODO: tests with pending debt redist. gain >0 - // TODO: tests that show total debt change under user ops - // TODO: Basic TCR and ICR getter tests - // TODO: Test total debt invariant holds i.e. (D + S * delta_T) == sum_of_all_entire_trove_debts. - // --- batchLiquidateTroves (Normal Mode, offset) --- function testBatchLiquidateTrovesPureOffsetChangesAggRecordedInterestCorrectly() public { @@ -2235,4 +2226,169 @@ contract InterestRateAggregate is DevTestSetup { // Check recorded debt sum reduced by C and D's entire debts assertEq(defaultPool.getBoldDebt(), debtInLiq); } + + // --- TCR tests --- + + function testGetTCRReturnsMaxUint256ForEmptySystem() public { + uint256 price = priceFeed.fetchPrice(); + console.log(price); + uint256 TCR = troveManager.getTCR(price); + + assertEq(TCR, MAX_UINT256); + } + + function testGetTCRReturnsICRofTroveForSystemWithOneTrove() public { + uint256 price = priceFeed.fetchPrice(); + uint256 troveDebtRequest = 2000e18; + uint256 coll = 20 ether; + uint256 interestRate = 25e16; + + openTroveNoHints100pctMaxFee(A, coll, troveDebtRequest, interestRate); + + uint256 compositeDebt = troveDebtRequest + borrowerOperations.BOLD_GAS_COMPENSATION(); + uint256 expectedICR = coll * price / compositeDebt; + assertEq(expectedICR, troveManager.getCurrentICR(A, price)); + + assertEq(expectedICR,troveManager.getTCR(price)); + } + + function testGetTCRReturnsSizeWeightedRatioForSystemWithMultipleTroves() public { + uint256 price = priceFeed.fetchPrice(); + console.log(price, "price"); + uint256 troveDebtRequest_A = 2000e18; + uint256 troveDebtRequest_B = 3000e18; + uint256 troveDebtRequest_C = 5000e18; + uint256 coll_A = 20 ether; + uint256 coll_B = 30 ether; + uint256 coll_C = 40 ether; + uint256 interestRate = 25e16; + + openTroveNoHints100pctMaxFee(A, coll_A, troveDebtRequest_A, interestRate); + openTroveNoHints100pctMaxFee(B, coll_B, troveDebtRequest_B, interestRate); + openTroveNoHints100pctMaxFee(C, coll_C, troveDebtRequest_C, interestRate); + + uint256 compositeDebt_A = troveDebtRequest_A + borrowerOperations.BOLD_GAS_COMPENSATION(); + uint256 compositeDebt_B = troveDebtRequest_B + borrowerOperations.BOLD_GAS_COMPENSATION(); + uint256 compositeDebt_C = troveDebtRequest_C + borrowerOperations.BOLD_GAS_COMPENSATION(); + + uint256 sizeWeightedCR = (coll_A + coll_B + coll_C) * price / (compositeDebt_A + compositeDebt_B + compositeDebt_C); + + assertEq(sizeWeightedCR, troveManager.getTCR(price)); + } + + function testGetTCRIncorporatesTroveInterestForSystemWithSingleTrove() public { + uint256 price = priceFeed.fetchPrice(); + uint256 troveDebtRequest = 2000e18; + uint256 coll = 20 ether; + uint256 interestRate = 25e16; + + openTroveNoHints100pctMaxFee(A, coll, troveDebtRequest, interestRate); + + // Fast-forward time + vm.warp(block.timestamp + 14 days); + + uint256 troveInterest = troveManager.calcTroveAccruedInterest(A); + assertGt(troveInterest, 0); + + uint256 compositeDebt = troveDebtRequest + borrowerOperations.BOLD_GAS_COMPENSATION() + troveInterest; + uint256 expectedICR = coll * price / compositeDebt; + assertEq(expectedICR, troveManager.getCurrentICR(A, price)); + + assertEq(expectedICR,troveManager.getTCR(price)); + } + + function testGetTCRIncorporatesAllTroveInterestForSystemWithMultipleTroves() public { + uint256 price = priceFeed.fetchPrice(); + console.log(price, "price"); + + // Use structs to bi-pass "stack-too-deep" error + TroveDebtRequests memory troveDebtRequests; + TroveCollAmounts memory troveCollAmounts; + TroveInterestRates memory troveInterestRates; + + troveDebtRequests.A = 2000e18; + troveDebtRequests.B = 4000e18; + troveDebtRequests.C = 5000e18; + troveCollAmounts.A = 20 ether; + troveCollAmounts.B = 30 ether; + troveCollAmounts.C = 40 ether; + + troveInterestRates.A = 25e16; + troveInterestRates.B = 25e16; + troveInterestRates.C = 25e16; + + openTroveNoHints100pctMaxFee(A, troveCollAmounts.A, troveDebtRequests.A, troveInterestRates.A); + // Fast-forward time + vm.warp(block.timestamp + 14 days); + openTroveNoHints100pctMaxFee(B, troveCollAmounts.B, troveDebtRequests.B, troveInterestRates.B); + // Fast-forward time + vm.warp(block.timestamp + 14 days); + openTroveNoHints100pctMaxFee(C, troveCollAmounts.C, troveDebtRequests.C, troveInterestRates.C); + // Fast-forward time + vm.warp(block.timestamp + 14 days); + + uint256 troveInterest_A = troveManager.calcTroveAccruedInterest(A); + assertGt(troveInterest_A, 0); + uint256 troveInterest_B = troveManager.calcTroveAccruedInterest(B); + assertGt(troveInterest_B, 0); + uint256 troveInterest_C = troveManager.calcTroveAccruedInterest(C); + assertGt(troveInterest_C, 0); + + uint256 compositeDebt_A = troveDebtRequests.A + borrowerOperations.BOLD_GAS_COMPENSATION() + troveInterest_A; + uint256 compositeDebt_B = troveDebtRequests.B + borrowerOperations.BOLD_GAS_COMPENSATION() + troveInterest_B; + uint256 compositeDebt_C = troveDebtRequests.C + borrowerOperations.BOLD_GAS_COMPENSATION() + troveInterest_C; + + uint256 expectedTCR = (troveCollAmounts.A + troveCollAmounts.B + troveCollAmounts.C) * price / (compositeDebt_A + compositeDebt_B + compositeDebt_C); + + assertEq(expectedTCR, troveManager.getTCR(price)); + } + + // --- ICR tests --- + + // - 0 for non-existent Trove + + function testGetCurrentICRReturnsInfinityForNonExistentTrove() public { + uint256 price = priceFeed.fetchPrice(); + uint256 ICR = troveManager.getCurrentICR(A, price); + + assertEq(ICR, MAX_UINT256); + } + + function testGetCurrentICRReturnsCorrectValueForNoInterest() public { + uint256 price = priceFeed.fetchPrice(); + uint256 troveDebtRequest = 2000e18; + uint256 coll = 20 ether; + uint256 interestRate = 25e16; + + openTroveNoHints100pctMaxFee(A, coll, troveDebtRequest, interestRate); + + uint256 compositeDebt = troveDebtRequest + borrowerOperations.BOLD_GAS_COMPENSATION(); + uint256 expectedICR = coll * price / compositeDebt; + assertEq(expectedICR, troveManager.getCurrentICR(A, price)); + } + + function testGetCurrentICRReturnsCorrectValueWithAccruedInterest() public { + uint256 price = priceFeed.fetchPrice(); + uint256 troveDebtRequest = 2000e18; + uint256 coll = 20 ether; + uint256 interestRate = 25e16; + + openTroveNoHints100pctMaxFee(A, coll, troveDebtRequest, interestRate); + + // Fast-forward time + vm.warp(block.timestamp + 14 days); + + uint256 troveInterest = troveManager.calcTroveAccruedInterest(A); + assertGt(troveInterest, 0); + + uint256 compositeDebt = troveDebtRequest + borrowerOperations.BOLD_GAS_COMPENSATION() + troveInterest; + uint256 expectedICR = coll * price / compositeDebt; + assertEq(expectedICR, troveManager.getCurrentICR(A, price)); + } + + // TODO: mixed collateral & debt adjustment opps + // TODO: tests with pending debt redist. gain >0 + // TODO: tests that show total debt change under user ops + // TODO: Test total debt invariant holds i.e. (D + S * delta_T) == sum_of_all_entire_trove_debts in + // more complex sequences of borrower ops and time passing } diff --git a/contracts/src/test/interestRateBasic.t.sol b/contracts/src/test/interestRateBasic.t.sol index e8ddb7bdb..3b80aedbe 100644 --- a/contracts/src/test/interestRateBasic.t.sol +++ b/contracts/src/test/interestRateBasic.t.sol @@ -713,23 +713,23 @@ contract InterestRateBasic is DevTestSetup { function testWithdrawETHGainToTroveDoesntChangeEntireTroveDebt() public { _setupForWithdrawETHGainToTrove(); - // // Fast-forward time - // vm.warp(block.timestamp + 90 days); + // Fast-forward time + vm.warp(block.timestamp + 90 days); - // (uint256 entireTroveDebt_1, , , , ) = troveManager.getEntireDebtAndColl(A); - // assertGt(entireTroveDebt_1, 0); + (uint256 entireTroveDebt_1, , , , ) = troveManager.getEntireDebtAndColl(A); + assertGt(entireTroveDebt_1, 0); - // console.log(troveManager.getCurrentICR(A, 1000e18), "A ICR before"); - // // A withdraws ETH gain to Trove - // withdrawETHGainToTrove(A); + console.log(troveManager.getCurrentICR(A, 1000e18), "A ICR before"); + // A withdraws ETH gain to Trove + withdrawETHGainToTrove(A); - // console.log(troveManager.getCurrentICR(A, 1000e18), "A ICR after"); + console.log(troveManager.getCurrentICR(A, 1000e18), "A ICR after"); - // (uint256 entireTroveDebt_2, , , , ) = troveManager.getEntireDebtAndColl(A); - // console.log(entireTroveDebt_2, "entireTroveDebt_2"); - // console.log(entireTroveDebt_1, "entireTroveDebt_1"); + (uint256 entireTroveDebt_2, , , , ) = troveManager.getEntireDebtAndColl(A); + console.log(entireTroveDebt_2, "entireTroveDebt_2"); + console.log(entireTroveDebt_1, "entireTroveDebt_1"); - // assertEq(entireTroveDebt_2, entireTroveDebt_1); + assertEq(entireTroveDebt_2, entireTroveDebt_1); } function testWithdrawETHGainToTroveIncreasesRecordedTroveDebtByAccruedInterest() public {