diff --git a/contracts/test/TestContracts/DevTestSetup.sol b/contracts/test/TestContracts/DevTestSetup.sol index df72683dc..221a0c39e 100644 --- a/contracts/test/TestContracts/DevTestSetup.sol +++ b/contracts/test/TestContracts/DevTestSetup.sol @@ -214,6 +214,13 @@ contract DevTestSetup is BaseTest { function _setupForRedemption(ABCDEF memory _troveInterestRates) internal returns (uint256 coll, uint256 debtRequest, ABCDEF memory troveIDs) + { + return _setupForRedemption(_troveInterestRates, false); + } + + function _setupForRedemption(ABCDEF memory _troveInterestRates, bool _batched) + internal + returns (uint256 coll, uint256 debtRequest, ABCDEF memory troveIDs) { priceFeed.setPrice(2000e18); @@ -222,10 +229,17 @@ contract DevTestSetup is BaseTest { coll = 20 ether; debtRequest = 20200e18; - troveIDs.A = openTroveNoHints100pct(A, coll, debtRequest, _troveInterestRates.A); - troveIDs.B = openTroveNoHints100pct(B, coll, debtRequest, _troveInterestRates.B); - troveIDs.C = openTroveNoHints100pct(C, coll, debtRequest, _troveInterestRates.C); - troveIDs.D = openTroveNoHints100pct(D, coll, debtRequest, _troveInterestRates.D); + if (_batched) { + troveIDs.A = openTroveAndJoinBatchManager(A, coll, debtRequest, A, _troveInterestRates.A); + troveIDs.B = openTroveAndJoinBatchManager(B, coll, debtRequest, B, _troveInterestRates.B); + troveIDs.C = openTroveAndJoinBatchManager(C, coll, debtRequest, C, _troveInterestRates.C); + troveIDs.D = openTroveAndJoinBatchManager(D, coll, debtRequest, D, _troveInterestRates.D); + } else { + troveIDs.A = openTroveNoHints100pct(A, coll, debtRequest, _troveInterestRates.A); + troveIDs.B = openTroveNoHints100pct(B, coll, debtRequest, _troveInterestRates.B); + troveIDs.C = openTroveNoHints100pct(C, coll, debtRequest, _troveInterestRates.C); + troveIDs.D = openTroveNoHints100pct(D, coll, debtRequest, _troveInterestRates.D); + } // A, B, C, D transfer all their Bold to E transferBold(A, E, boldToken.balanceOf(A)); @@ -244,6 +258,16 @@ contract DevTestSetup is BaseTest { return _setupForRedemption(troveInterestRates); } + function _setupForRedemptionAscendingInterestInBatch() internal returns (uint256, uint256, ABCDEF memory) { + ABCDEF memory troveInterestRates; + troveInterestRates.A = 1e17; // 10% + troveInterestRates.B = 2e17; // 20% + troveInterestRates.C = 3e17; // 30% + troveInterestRates.D = 4e17; // 40% + + return _setupForRedemption(troveInterestRates, true); + } + function _redeemAndCreateZombieTrovesAAndB(ABCDEF memory _troveIDs) internal { // Wait some time to make sure redemption rate low vm.warp(block.timestamp + 30 days); diff --git a/contracts/test/redemptions.t.sol b/contracts/test/redemptions.t.sol index 6c32c3da6..a4a029a4a 100644 --- a/contracts/test/redemptions.t.sol +++ b/contracts/test/redemptions.t.sol @@ -467,6 +467,32 @@ contract Redemptions is DevTestSetup { assertEq(troveManager.lastZombieTroveId(), 0, "Wrong last zombie trove pointer after"); } + function testZombieTrovePointerGetsResetIfTroveIsClosedFromABatch() public { + (,, ABCDEF memory troveIDs) = _setupForRedemptionAscendingInterestInBatch(); + + _redeemAndCreateZombieTrovesAAndB(troveIDs); + + // Check last Zombie trove pointer + assertEq(troveManager.lastZombieTroveId(), troveIDs.B, "Wrong last zombie trove pointer before"); + + // Get B debt before 2nd redeem + uint256 debt_B = troveManager.getTroveEntireDebt(troveIDs.B); + assertGt(debt_B, 0, "B debt should be non zero"); + + deal(address(boldToken), B, debt_B); + closeTrove(B, troveIDs.B); + + // Check B is closed + assertEq( + uint8(troveManager.getTroveStatus(troveIDs.B)), + uint8(ITroveManager.Status.closedByOwner), + "B trove should be closed" + ); + + // Check last Zombie trove pointer + assertEq(troveManager.lastZombieTroveId(), 0, "Wrong last zombie trove pointer after"); + } + function testZombieTrovePointerGetsResetIfTroveIsLiquidated() public { (,, ABCDEF memory troveIDs) = _setupForRedemptionAscendingInterest(); diff --git a/contracts/test/shutdown.t.sol b/contracts/test/shutdown.t.sol index 8dd84cb00..e7103a2fc 100644 --- a/contracts/test/shutdown.t.sol +++ b/contracts/test/shutdown.t.sol @@ -371,8 +371,25 @@ contract ShutdownTest is DevTestSetup { function testCannotUrgentRedeemZero() public { uint256 troveId = prepareAndShutdownFirstBranch(); + vm.startPrank(A); vm.expectRevert(TroveManager.ZeroAmount.selector); troveManager.urgentRedemption(0, uintToArray(troveId), 0); + vm.stopPrank(); + } + + function testCannotUrgentRedeemWithoutEnoughBalance() public { + uint256 troveId = prepareAndShutdownFirstBranch(); + + // A sends 900 Bold to B + vm.startPrank(A); + boldToken.transfer(B, 900e18); + vm.stopPrank(); + + // B tries to redeem 1000 Bold + vm.startPrank(B); + vm.expectRevert(TroveManager.NotEnoughBoldBalance.selector); + troveManager.urgentRedemption(1000e18, uintToArray(troveId), 0); + vm.stopPrank(); } function testUrgentRedeemRevertsIfMinNotReached() public { diff --git a/contracts/test/troveManager.t.sol b/contracts/test/troveManager.t.sol index d0c07ffd2..621383f88 100644 --- a/contracts/test/troveManager.t.sol +++ b/contracts/test/troveManager.t.sol @@ -5,6 +5,120 @@ pragma solidity 0.8.24; import "./TestContracts/DevTestSetup.sol"; contract TroveManagerTest is DevTestSetup { + function testOnlyCollateralRegistryCanCallRedeem() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotCollateralRegistry.selector); + troveManager.redeemCollateral(A, 1, 2000e18, 1e16, 100); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallShutdown() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + troveManager.shutdown(); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnOpenTrove() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onOpenTrove(A, addressToTroveId(A), troveChange, 5e16); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnOpenTroveAndJoinBatch() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onOpenTroveAndJoinBatch(A, addressToTroveId(A), troveChange, B, 10000e18, 5000e18); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallSetTroveStatusToActive() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + troveManager.setTroveStatusToActive(addressToTroveId(A)); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnAdjustTroveInterestRate() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onAdjustTroveInterestRate(addressToTroveId(A), 10000e18, 5000e18, 6e16, troveChange); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnAdjustTrove() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onAdjustTrove(addressToTroveId(A), 10000e18, 5000e18, troveChange); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnCloseTrove() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onCloseTrove(addressToTroveId(A), troveChange, B, 10000e18, 5000e18); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnAdjustTroveInsideBatch() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onAdjustTroveInsideBatch(addressToTroveId(A), 10000e18, 5000e18, troveChange, B, 10000e18, 5000e18); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnApplyTroveInterest() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onApplyTroveInterest(addressToTroveId(A), 10000e18, 5000e18, B, 10000e18, 5000e18, troveChange); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnRegisterBatchManager() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + troveManager.onRegisterBatchManager(A, 5e16, 5e14); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnLowerBatchManagerAnnualFee() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + troveManager.onLowerBatchManagerAnnualFee(A, 10000e18, 5000e18, 5e14); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnSetBatchManagerAnnualInterestRate() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + troveManager.onSetBatchManagerAnnualInterestRate(A, 10000e18, 5000e18, 5e14, 100e18); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnSetInterestBatchManager() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveManager.OnSetInterestBatchManagerParams memory params; + troveManager.onSetInterestBatchManager(params); + vm.stopPrank(); + } + + function testOnlyBorrowerOperationsCanCallOnRemoveFromBatch() public { + vm.startPrank(A); + vm.expectRevert(TroveManager.CallerNotBorrowerOperations.selector); + TroveChange memory troveChange; + troveManager.onRemoveFromBatch(addressToTroveId(A), 10000e18, 5000e18, troveChange, B, 10000e18, 5000e18, 5e14); + vm.stopPrank(); + } + function testLiquidateLastTroveReverts() public { priceFeed.setPrice(2_000e18); uint256 ATroveId = openTroveNoHints100pct(A, 100 ether, 100_000e18, 1e17);