Skip to content

Commit

Permalink
Add borrower-chosen interest rate ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
RickGriff committed Jan 25, 2024
1 parent 2658003 commit 1d26133
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 175 deletions.
96 changes: 48 additions & 48 deletions contracts/src/BorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
uint netDebt;
uint compositeDebt;
uint ICR;
uint NICR;
uint stake;
uint arrayIndex;
}
Expand Down Expand Up @@ -154,16 +153,19 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

// --- Borrower Trove Operations ---

function openTrove(uint _maxFeePercentage, uint _boldAmount, address _upperHint, address _lowerHint) external payable override {
function openTrove(uint _maxFeePercentage, uint _boldAmount, address _upperHint, address _lowerHint, uint256 _annualInterestRate) external payable override {
ContractsCache memory contractsCache = ContractsCache(troveManager, activePool, boldToken);
LocalVariables_openTrove memory vars;

vars.price = priceFeed.fetchPrice();
bool isRecoveryMode = _checkRecoveryMode(vars.price);

_requireValidAnnualInterestRate(_annualInterestRate);
_requireValidMaxFeePercentage(_maxFeePercentage, isRecoveryMode);
_requireTroveisNotActive(contractsCache.troveManager, msg.sender);

// TODO: apply aggregate pending interest, and take snapshot of current timestamp.

vars.BoldFee;
vars.netDebt = _boldAmount;

Expand All @@ -177,7 +179,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
assert(vars.compositeDebt > 0);

vars.ICR = LiquityMath._computeCR(msg.value, vars.compositeDebt, vars.price);
vars.NICR = LiquityMath._computeNominalCR(msg.value, vars.compositeDebt);

if (isRecoveryMode) {
_requireICRisAboveCCR(vars.ICR);
Expand All @@ -187,15 +188,15 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
_requireNewTCRisAboveCCR(newTCR);
}

// Set the trove struct's properties
contractsCache.troveManager.setTroveStatus(msg.sender, 1);
contractsCache.troveManager.increaseTroveColl(msg.sender, msg.value);
contractsCache.troveManager.increaseTroveDebt(msg.sender, vars.compositeDebt);

contractsCache.troveManager.updateTroveRewardSnapshots(msg.sender);
vars.stake = contractsCache.troveManager.updateStakeAndTotalStakes(msg.sender);
// Set the stored Trove properties
vars.stake = contractsCache.troveManager.setTrovePropertiesOnOpen(
msg.sender,
msg.value,
vars.compositeDebt,
_annualInterestRate
);

sortedTroves.insert(msg.sender, vars.NICR, _upperHint, _lowerHint);
sortedTroves.insert(msg.sender, _annualInterestRate, _upperHint, _lowerHint);
vars.arrayIndex = contractsCache.troveManager.addTroveOwnerToArray(msg.sender);
emit TroveCreated(msg.sender, vars.arrayIndex);

Expand All @@ -210,33 +211,48 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
}

// Send ETH as collateral to a trove
function addColl(address _upperHint, address _lowerHint) external payable override {
_adjustTrove(msg.sender, 0, 0, false, _upperHint, _lowerHint, 0);
function addColl() external payable override {
_adjustTrove(msg.sender, 0, 0, false, 0);
}

// Send ETH as collateral to a trove. Called by only the Stability Pool.
function moveETHGainToTrove(address _borrower, address _upperHint, address _lowerHint) external payable override {
function moveETHGainToTrove(address _borrower) external payable override {
_requireCallerIsStabilityPool();
_adjustTrove(_borrower, 0, 0, false, _upperHint, _lowerHint, 0);
_adjustTrove(_borrower, 0, 0, false, 0);
}

// Withdraw ETH collateral from a trove
function withdrawColl(uint _collWithdrawal, address _upperHint, address _lowerHint) external override {
_adjustTrove(msg.sender, _collWithdrawal, 0, false, _upperHint, _lowerHint, 0);
function withdrawColl(uint _collWithdrawal) external override {
_adjustTrove(msg.sender, _collWithdrawal, 0, false, 0);
}

// Withdraw Bold tokens from a trove: mint new Bold tokens to the owner, and increase the trove's debt accordingly
function withdrawBold(uint _maxFeePercentage, uint _boldAmount, address _upperHint, address _lowerHint) external override {
_adjustTrove(msg.sender, 0, _boldAmount, true, _upperHint, _lowerHint, _maxFeePercentage);
function withdrawBold(uint _maxFeePercentage, uint _boldAmount ) external override {
_adjustTrove(msg.sender, 0, _boldAmount, true, _maxFeePercentage);
}

// Repay Bold tokens to a Trove: Burn the repaid Bold tokens, and reduce the trove's debt accordingly
function repayBold(uint _boldAmount, address _upperHint, address _lowerHint) external override {
_adjustTrove(msg.sender, 0, _boldAmount, false, _upperHint, _lowerHint, 0);
function repayBold(uint _boldAmount) external override {
_adjustTrove(msg.sender, 0, _boldAmount, false, 0);
}

function adjustTrove(uint _maxFeePercentage, uint _collWithdrawal, uint _boldChange, bool _isDebtIncrease, address _upperHint, address _lowerHint) external payable override {
_adjustTrove(msg.sender, _collWithdrawal, _boldChange, _isDebtIncrease, _upperHint, _lowerHint, _maxFeePercentage);
function adjustTrove(uint _maxFeePercentage, uint _collWithdrawal, uint _boldChange, bool _isDebtIncrease) external payable override {
_adjustTrove(msg.sender, _collWithdrawal, _boldChange, _isDebtIncrease, _maxFeePercentage);
}

function adjustTroveInterestRate(uint _newAnnualInterestRate, address _upperHint, address _lowerHint) external {
_requireValidAnnualInterestRate(_newAnnualInterestRate);
ITroveManager troveManagerCached = troveManager;
_requireTroveisActive(troveManagerCached, msg.sender);

// TODO: apply individual and aggregate pending interest, and take snapshots of current timestamp.
// TODO: determine how applying pending interest should interact / be sequenced with applying pending rewards from redistributions.

troveManagerCached.applyPendingRewards(msg.sender);

sortedTroves.reInsert(msg.sender, _newAnnualInterestRate, _upperHint, _lowerHint);

troveManagerCached.changeAnnualInterestRate(msg.sender, _newAnnualInterestRate);
}

/*
Expand All @@ -246,7 +262,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
*
* If both are positive, it will revert.
*/
function _adjustTrove(address _borrower, uint _collWithdrawal, uint _boldChange, bool _isDebtIncrease, address _upperHint, address _lowerHint, uint _maxFeePercentage) internal {
function _adjustTrove(address _borrower, uint _collWithdrawal, uint _boldChange, bool _isDebtIncrease, uint _maxFeePercentage) internal {
ContractsCache memory contractsCache = ContractsCache(troveManager, activePool, boldToken);
LocalVariables_adjustTrove memory vars;

Expand All @@ -264,6 +280,8 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
// Confirm the operation is either a borrower adjusting their own trove, or a pure ETH transfer from the Stability Pool to a trove
assert(msg.sender == _borrower || (msg.sender == stabilityPoolAddress && msg.value > 0 && _boldChange == 0));

// TODO: apply individual and aggregate pending interest, and take snapshots of current timestamp.

contractsCache.troveManager.applyPendingRewards(_borrower);

// Get the collChange based on whether or not ETH was sent in the transaction
Expand Down Expand Up @@ -297,10 +315,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
(vars.newColl, vars.newDebt) = _updateTroveFromAdjustment(contractsCache.troveManager, _borrower, vars.collChange, vars.isCollIncrease, vars.netDebtChange, _isDebtIncrease);
vars.stake = contractsCache.troveManager.updateStakeAndTotalStakes(_borrower);

// Re-insert trove in to the sorted list
uint newNICR = _getNewNominalICRFromTroveChange(vars.coll, vars.debt, vars.collChange, vars.isCollIncrease, vars.netDebtChange, _isDebtIncrease);
sortedTroves.reInsert(_borrower, newNICR, _upperHint, _lowerHint);

emit TroveUpdated(_borrower, vars.newDebt, vars.newColl, vars.stake, BorrowerOperation.adjustTrove);
emit BoldBorrowingFeePaid(msg.sender, vars.BoldFee);

Expand All @@ -326,6 +340,8 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
uint price = priceFeed.fetchPrice();
_requireNotInRecoveryMode(price);

// TODO: apply individual and aggregate pending interest, and take snapshots of current timestamp.

troveManagerCached.applyPendingRewards(msg.sender);

uint coll = troveManagerCached.getTroveColl(msg.sender);
Expand Down Expand Up @@ -560,28 +576,12 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
}
}

// --- ICR and TCR getters ---

// Compute the new collateral ratio, considering the change in coll and debt. Assumes 0 pending rewards.
function _getNewNominalICRFromTroveChange
(
uint _coll,
uint _debt,
uint _collChange,
bool _isCollIncrease,
uint _debtChange,
bool _isDebtIncrease
)
pure
internal
returns (uint)
{
(uint newColl, uint newDebt) = _getNewTroveAmounts(_coll, _debt, _collChange, _isCollIncrease, _debtChange, _isDebtIncrease);

uint newNICR = LiquityMath._computeNominalCR(newColl, newDebt);
return newNICR;
function _requireValidAnnualInterestRate(uint256 _annualInterestRate) internal pure {
require(_annualInterestRate <= MAX_ANNUAL_INTEREST_RATE, "Interest rate must not be greater than max");
}

// --- ICR and TCR getters ---

// Compute the new collateral ratio, considering the change in coll and debt. Assumes 0 pending rewards.
function _getNewICRFromTroveChange
(
Expand Down
2 changes: 2 additions & 0 deletions contracts/src/Dependencies/LiquityBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ contract LiquityBase is BaseMath, ILiquityBase {
uint constant public MIN_NET_DEBT = 1800e18;
// uint constant public MIN_NET_DEBT = 0;

uint256 constant public MAX_ANNUAL_INTEREST_RATE = 1e18; // 100%

uint constant public PERCENT_DIVISOR = 200; // dividing by 200 yields 0.5%

uint constant public BORROWING_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%
Expand Down
16 changes: 9 additions & 7 deletions contracts/src/Interfaces/IBorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,25 @@ interface IBorrowerOperations is ILiquityBase {
address _lqtyStakingAddress
) external;

function openTrove(uint _maxFee, uint _boldAmount, address _upperHint, address _lowerHint) external payable;
function openTrove(uint _maxFee, uint _boldAmount, address _upperHint, address _lowerHint, uint256 _annualInterestRate) external payable;

function addColl(address _upperHint, address _lowerHint) external payable;
function addColl() external payable;

function moveETHGainToTrove(address _user, address _upperHint, address _lowerHint) external payable;
function moveETHGainToTrove(address _user) external payable;

function withdrawColl(uint _amount, address _upperHint, address _lowerHint) external;
function withdrawColl(uint _amount) external;

function withdrawBold(uint _maxFee, uint _amount, address _upperHint, address _lowerHint) external;
function withdrawBold(uint _maxFee, uint _amount) external;

function repayBold(uint _amount, address _upperHint, address _lowerHint) external;
function repayBold(uint _amount) external;

function closeTrove() external;

function adjustTrove(uint _maxFee, uint _collWithdrawal, uint _debtChange, bool isDebtIncrease, address _upperHint, address _lowerHint) external payable;
function adjustTrove(uint _maxFee, uint _collWithdrawal, uint _debtChange, bool isDebtIncrease) external payable;

function claimCollateral() external;

function getCompositeDebt(uint _debt) external pure returns (uint);

function adjustTroveInterestRate(uint _newAnnualInterestRate, address _upperHint, address _lowerHint) external;
}
2 changes: 1 addition & 1 deletion contracts/src/Interfaces/IStabilityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ interface IStabilityPool is ILiquityBase {
* - Leaves their compounded deposit in the Stability Pool
* - Takes new snapshots of accumulators P and S
*/
function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external;
function withdrawETHGainToTrove() external;

/*
* Initial checks:
Expand Down
8 changes: 5 additions & 3 deletions contracts/src/Interfaces/ITroveManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ interface ITroveManager is ILiquityBase {

function updateStakeAndTotalStakes(address _borrower) external returns (uint);

function updateTroveRewardSnapshots(address _borrower) external;

function addTroveOwnerToArray(address _borrower) external returns (uint index);

function applyPendingRewards(address _borrower) external;
Expand Down Expand Up @@ -104,7 +102,9 @@ interface ITroveManager is ILiquityBase {

function getTroveColl(address _borrower) external view returns (uint);

function setTroveStatus(address _borrower, uint num) external;
function getTroveAnnualInterestRate(address _borrower) external view returns (uint);

function setTrovePropertiesOnOpen(address _borrower, uint256 _coll, uint256 _debt, uint256 _annualInterestRate) external returns (uint256);

function increaseTroveColl(address _borrower, uint _collIncrease) external returns (uint);

Expand All @@ -114,6 +114,8 @@ interface ITroveManager is ILiquityBase {

function decreaseTroveDebt(address _borrower, uint _collDecrease) external returns (uint);

function changeAnnualInterestRate(address _borrower, uint256 _newAnnualInterestRate) external returns (uint256);

function getTCR(uint _price) external view returns (uint);

function checkRecoveryMode(uint _price) external view returns (bool);
Expand Down
6 changes: 4 additions & 2 deletions contracts/src/MultiTroveGetter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ contract MultiTroveGetter {
_troves[idx].coll,
_troves[idx].stake,
/* status */,
/* arrayIndex */
/* arrayIndex */,
/* annualInterestRate */
) = troveManager.Troves(currentTroveowner);
(
_troves[idx].snapshotETH,
Expand Down Expand Up @@ -107,7 +108,8 @@ contract MultiTroveGetter {
_troves[idx].coll,
_troves[idx].stake,
/* status */,
/* arrayIndex */
/* arrayIndex */,
/* annualInterestRate */
) = troveManager.Troves(currentTroveowner);
(
_troves[idx].snapshotETH,
Expand Down
Loading

0 comments on commit 1d26133

Please sign in to comment.