Skip to content

Commit

Permalink
Merge pull request #403 from liquity/dedaub_L6
Browse files Browse the repository at this point in the history
Add checks to setInterestIndividualDelegate
  • Loading branch information
bingen authored Sep 4, 2024
2 parents fd9b1cd + ef2c751 commit e9cc36d
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 40 deletions.
92 changes: 52 additions & 40 deletions contracts/src/BorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -816,11 +816,19 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
uint256 _maxUpfrontFee
) external {
_requireIsNotShutDown();
_requireTroveIsActive(troveManager, _troveId);
_requireCallerIsBorrower(_troveId);
_requireValidAnnualInterestRate(_minInterestRate);
_requireValidAnnualInterestRate(_maxInterestRate);
// With the check below, it could only be ==
_requireOrderedRange(_minInterestRate, _maxInterestRate);

interestIndividualDelegateOf[_troveId] =
InterestIndividualDelegate(_delegate, _minInterestRate, _maxInterestRate);
// Can’t have both individual delegation and batch manager
if (interestBatchManagerOf[_troveId] != address(0)) {
// Not needed, implicitly checked in removeFromBatch
//_requireValidAnnualInterestRate(_newAnnualInterestRate);
removeFromBatch(_troveId, _newAnnualInterestRate, _upperHint, _lowerHint, _maxUpfrontFee);
}
}
Expand All @@ -846,7 +854,7 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
_requireValidAnnualInterestRate(_minInterestRate);
_requireValidAnnualInterestRate(_maxInterestRate);
// With the check below, it could only be ==
if (_minInterestRate >= _maxInterestRate) revert MinGeMax();
_requireOrderedRange(_minInterestRate, _maxInterestRate);
_requireInterestRateInRange(_currentInterestRate, _minInterestRate, _maxInterestRate);
// Not needed, implicitly checked in the condition above:
//_requireValidAnnualInterestRate(_currentInterestRate);
Expand Down Expand Up @@ -1295,45 +1303,6 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
return batchManager;
}

function _requireInterestRateInDelegateRange(uint256 _troveId, uint256 _annualInterestRate) internal view {
InterestIndividualDelegate memory individualDelegate = interestIndividualDelegateOf[_troveId];
if (individualDelegate.account != address(0)) {
_requireInterestRateInRange(
_annualInterestRate, individualDelegate.minInterestRate, individualDelegate.maxInterestRate
);
}
}

function _requireInterestRateInBatchManagerRange(address _interestBatchManagerAddress, uint256 _annualInterestRate)
internal
view
{
InterestBatchManager memory interestBatchManager = interestBatchManagers[_interestBatchManagerAddress];
_requireInterestRateInRange(
_annualInterestRate, interestBatchManager.minInterestRate, interestBatchManager.maxInterestRate
);
}

function _requireInterestRateInRange(
uint256 _annualInterestRate,
uint256 _minInterestRate,
uint256 _maxInterestRate
) internal pure {
if (_minInterestRate > _annualInterestRate || _annualInterestRate > _maxInterestRate) {
revert InterestNotInRange();
}
}

function _requireInterestRateChangePeriodPassed(
address _interestBatchManagerAddress,
uint256 _lastInterestRateAdjTime
) internal view {
InterestBatchManager memory interestBatchManager = interestBatchManagers[_interestBatchManagerAddress];
if (block.timestamp < _lastInterestRateAdjTime + uint256(interestBatchManager.minInterestRateChangePeriod)) {
revert BatchInterestRateChangePeriodNotPassed();
}
}

function _requireTroveIsOpen(ITroveManager _troveManager, uint256 _troveId) internal view {
ITroveManager.Status status = _troveManager.getTroveStatus(_troveId);
if (status != ITroveManager.Status.active && status != ITroveManager.Status.unredeemable) {
Expand Down Expand Up @@ -1470,6 +1439,49 @@ contract BorrowerOperations is LiquityBase, AddRemoveManagers, IBorrowerOperatio
}
}

function _requireOrderedRange(uint256 _minInterestRate, uint256 _maxInterestRate) internal pure {
if (_minInterestRate >= _maxInterestRate) revert MinGeMax();
}

function _requireInterestRateInDelegateRange(uint256 _troveId, uint256 _annualInterestRate) internal view {
InterestIndividualDelegate memory individualDelegate = interestIndividualDelegateOf[_troveId];
if (individualDelegate.account != address(0)) {
_requireInterestRateInRange(
_annualInterestRate, individualDelegate.minInterestRate, individualDelegate.maxInterestRate
);
}
}

function _requireInterestRateInBatchManagerRange(address _interestBatchManagerAddress, uint256 _annualInterestRate)
internal
view
{
InterestBatchManager memory interestBatchManager = interestBatchManagers[_interestBatchManagerAddress];
_requireInterestRateInRange(
_annualInterestRate, interestBatchManager.minInterestRate, interestBatchManager.maxInterestRate
);
}

function _requireInterestRateInRange(
uint256 _annualInterestRate,
uint256 _minInterestRate,
uint256 _maxInterestRate
) internal pure {
if (_minInterestRate > _annualInterestRate || _annualInterestRate > _maxInterestRate) {
revert InterestNotInRange();
}
}

function _requireInterestRateChangePeriodPassed(
address _interestBatchManagerAddress,
uint256 _lastInterestRateAdjTime
) internal view {
InterestBatchManager memory interestBatchManager = interestBatchManagers[_interestBatchManagerAddress];
if (block.timestamp < _lastInterestRateAdjTime + uint256(interestBatchManager.minInterestRateChangePeriod)) {
revert BatchInterestRateChangePeriodNotPassed();
}
}

function _requireValidInterestBatchManager(address _interestBatchManagerAddress) internal view {
if (interestBatchManagers[_interestBatchManagerAddress].maxInterestRate == 0) {
revert InvalidInterestBatchManager();
Expand Down
134 changes: 134 additions & 0 deletions contracts/src/test/interestIndividualDelegation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,140 @@ contract InterestIndividualDelegationTest is DevTestSetup {
vm.stopPrank();
}

function testSetDelegateRevertsIfTroveIsClosed() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Open a second one, so it’s not the last one and to have BOLD for interest
openTroveNoHints100pctWithIndex(A, 1, 100e18, 5000e18, 5e16);
// Close trove
closeTrove(A, troveId);

// Set batch manager (B)
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.TroveNotActive.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 1e16, 20e16, 0, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRevertsIfTroveIsUnredeemable() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Make trove unredeemable
redeem(A, 4000e18);
// Check A’s trove is unredeemable
assertEq(troveManager.checkTroveIsUnredeemable(troveId), true, "A trove should be unredeemable");

// Set batch manager (B)
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.TroveNotActive.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 1e16, 20e16, 0, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRevertsIfMinTooLow() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Set batch manager (B)
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.InterestRateTooLow.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 1e14, 20e16, 0, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRevertsIfMaxTooHigh() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Set batch manager (B)
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.InterestRateTooHigh.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 1e16, 101e16, 0, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRevertsIfMinEqMax() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Set batch manager (B)
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.MinGeMax.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 20e16, 20e16, 0, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRevertsIfMinGtMax() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Set batch manager (B)
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.MinGeMax.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 21e16, 20e16, 0, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRevertsIfNewInterestRateNotInRangeBelow() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Set batch manager (B)
vm.startPrank(A);
borrowerOperations.setInterestBatchManager(troveId, B, 0, 0, 1e24);
vm.stopPrank();

// Try to switch to individual delegate (C) along with new interest
uint256 newAnnualInterestRate = 1e14;
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.InterestRateTooLow.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 1e16, 20e16, newAnnualInterestRate, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRevertsIfNewInterestRateNotInRangeAbove() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
vm.stopPrank();

// Open trove
uint256 troveId = openTroveNoHints100pct(A, 100e18, 5000e18, 5e16);
// Set batch manager (B)
vm.startPrank(A);
borrowerOperations.setInterestBatchManager(troveId, B, 0, 0, 1e24);
vm.stopPrank();

// Try to switch to individual delegate (C) along with new interest
uint256 newAnnualInterestRate = 101e16;
vm.startPrank(A);
vm.expectRevert(BorrowerOperations.InterestRateTooHigh.selector);
borrowerOperations.setInterestIndividualDelegate(troveId, C, 1e16, 20e16, newAnnualInterestRate, 0, 0, 10000e18);
vm.stopPrank();
}

function testSetDelegateRemovesBatchManager() public {
vm.startPrank(B);
borrowerOperations.registerBatchManager(1e16, 20e16, 5e16, 25e14, MIN_INTEREST_RATE_CHANGE_PERIOD);
Expand Down

0 comments on commit e9cc36d

Please sign in to comment.