Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce liquidation penalty #157

Merged
merged 15 commits into from
May 15, 2024
Merged
76 changes: 50 additions & 26 deletions contracts/src/BorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
// --- Connected contract declarations ---

IERC20 public immutable ETH;
ITroveManager public troveManager;
ITroveManager public immutable troveManager;
address stabilityPoolAddress;
address gasPoolAddress;
ICollSurplusPool collSurplusPool;
IBoldToken public boldToken;
// A doubly linked list of Troves, sorted by their collateral ratios
ISortedTroves public sortedTroves;

// Minimum collateral ratio for individual troves
uint256 public immutable MCR;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use a constant here - it would save (a bit) of gas when we read the MCR value right?

Though I guess this approach makes deployment more robust as we can pass those Trove params.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, specially because we plan to have different MCRs for different collaterals.


/* --- Variable container structs ---

Used to hold, return and assign variables inside a function, in order to avoid the error:
Expand Down Expand Up @@ -101,15 +104,21 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
);
event BoldBorrowingFeePaid(uint256 indexed _troveId, uint256 _boldFee);

constructor(address _ETHAddress) {
checkContract(_ETHAddress);
ETH = IERC20(_ETHAddress);
constructor(IERC20 _ETH, ITroveManager _troveManager) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is moving TM into constructor related to the reduced liq penalty, or an independent improvement? (fine if so, just curious)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the reason is to fetch MCR from TroveManager, thus avoiding to define it twice (and making sure it’s consistent).

checkContract(address(_ETH));
checkContract(address(_troveManager));

ETH = _ETH;
troveManager = _troveManager;

MCR = _troveManager.MCR();

emit TroveManagerAddressChanged(address(_troveManager));
}

// --- Dependency setters ---

function setAddresses(
address _troveManagerAddress,
address _activePoolAddress,
address _defaultPoolAddress,
address _stabilityPoolAddress,
Expand All @@ -122,7 +131,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
// This makes impossible to open a trove with zero withdrawn Bold
assert(MIN_NET_DEBT > 0);

checkContract(_troveManagerAddress);
checkContract(_activePoolAddress);
checkContract(_defaultPoolAddress);
checkContract(_stabilityPoolAddress);
Expand All @@ -132,7 +140,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
checkContract(_sortedTrovesAddress);
checkContract(_boldTokenAddress);

troveManager = ITroveManager(_troveManagerAddress);
activePool = IActivePool(_activePoolAddress);
defaultPool = IDefaultPool(_defaultPoolAddress);
stabilityPoolAddress = _stabilityPoolAddress;
Expand All @@ -142,7 +149,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
sortedTroves = ISortedTroves(_sortedTrovesAddress);
boldToken = IBoldToken(_boldTokenAddress);

emit TroveManagerAddressChanged(_troveManagerAddress);
emit ActivePoolAddressChanged(_activePoolAddress);
emit DefaultPoolAddressChanged(_defaultPoolAddress);
emit StabilityPoolAddressChanged(_stabilityPoolAddress);
Expand Down Expand Up @@ -278,7 +284,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
// TODO: Use oldDebt and assert in fuzzing, remove before deployment
uint256 oldDebt = troveManager.getTroveEntireDebt(_troveId);
_adjustTrove(msg.sender, _troveId, 0, false, _boldAmount, false, 0, contractsCache);
assert(troveManager.getTroveEntireDebt(_troveId) < oldDebt);
assert(troveManager.getTroveEntireDebt(_troveId) < oldDebt);
}

function adjustTrove(
Expand All @@ -292,12 +298,19 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
ContractsCacheTMAPBT memory contractsCache = ContractsCacheTMAPBT(troveManager, activePool, boldToken);
_requireTroveIsActive(contractsCache.troveManager, _troveId);
_adjustTrove(
msg.sender, _troveId, _collChange, _isCollIncrease, _boldChange, _isDebtIncrease, _maxFeePercentage, contractsCache
msg.sender,
_troveId,
_collChange,
_isCollIncrease,
_boldChange,
_isDebtIncrease,
_maxFeePercentage,
contractsCache
);
}

function adjustUnredeemableTrove(
uint256 _troveId,
uint256 _troveId,
uint256 _maxFeePercentage,
uint256 _collChange,
bool _isCollIncrease,
Expand All @@ -308,12 +321,21 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
) external override {
ContractsCacheTMAPBT memory contractsCache = ContractsCacheTMAPBT(troveManager, activePool, boldToken);
_requireTroveIsUnredeemable(contractsCache.troveManager, _troveId);
// TODO: Gas - pass the cached TM down here, since we fetch it again inside _adjustTrove?
// TODO: Gas - pass the cached TM down here, since we fetch it again inside _adjustTrove?
_adjustTrove(
msg.sender, _troveId, _collChange, _isCollIncrease, _boldChange, _isDebtIncrease, _maxFeePercentage, contractsCache
msg.sender,
_troveId,
_collChange,
_isCollIncrease,
_boldChange,
_isDebtIncrease,
_maxFeePercentage,
contractsCache
);
troveManager.setTroveStatusToActive(_troveId);
sortedTroves.insert(_troveId, contractsCache.troveManager.getTroveAnnualInterestRate(_troveId), _upperHint, _lowerHint);
sortedTroves.insert(
_troveId, contractsCache.troveManager.getTroveAnnualInterestRate(_troveId), _upperHint, _lowerHint
);
}

function adjustTroveInterestRate(
Expand Down Expand Up @@ -545,11 +567,9 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
/**
* Claim remaining collateral from a redemption or from a liquidation with ICR > MCR in Recovery Mode
bingen marked this conversation as resolved.
Show resolved Hide resolved
*/
function claimCollateral(uint256 _troveId) external override {
_requireIsOwner(_troveId);

function claimCollateral() external override {
// send ETH from CollSurplus Pool to owner
collSurplusPool.claimColl(msg.sender, _troveId);
collSurplusPool.claimColl(msg.sender);
}

// --- Helper functions ---
Expand Down Expand Up @@ -632,17 +652,19 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
uint256 _boldChange,
bool _isDebtIncrease
) internal {
if (_isDebtIncrease) { // implies _boldChange > 0
if (_isDebtIncrease) {
// implies _boldChange > 0
address borrower = _troveManager.ownerOf(_troveId);
_boldToken.mint(borrower, _boldChange);
} else if (_boldChange > 0 ){
} else if (_boldChange > 0) {
_boldToken.burn(msg.sender, _boldChange);
}

if (_isCollIncrease) { // implies _collChange > 0
if (_isCollIncrease) {
// implies _collChange > 0
// Pull ETH tokens from sender and move them to the Active Pool
_pullETHAndSendToActivePool(_activePool, _collChange);
} else if (_collChange > 0 ){
} else if (_collChange > 0) {
address borrower = _troveManager.ownerOf(_troveId);
// Pull ETH from Active Pool and decrease its recorded ETH balance
_activePool.sendETH(borrower, _collChange);
Expand Down Expand Up @@ -709,11 +731,13 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
}

function _requireTroveIsUnredeemable(ITroveManager _troveManager, uint256 _troveId) internal view {
require(_troveManager.checkTroveIsUnredeemable(_troveId), "BorrowerOps: Trove does not have unredeemable status");
require(
_troveManager.checkTroveIsUnredeemable(_troveId), "BorrowerOps: Trove does not have unredeemable status"
);
}

function _requireTroveIsNotOpen(ITroveManager _troveManager, uint256 _troveId) internal view {
require(!_troveManager.checkTroveIsOpen(_troveId),"BorrowerOps: Trove is open");
function _requireTroveIsNotOpen(ITroveManager _troveManager, uint256 _troveId) internal view {
require(!_troveManager.checkTroveIsOpen(_troveId), "BorrowerOps: Trove is open");
}

function _requireNonZeroCollChange(uint256 _collChange) internal pure {
Expand Down Expand Up @@ -770,7 +794,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
}
}

function _requireICRisAboveMCR(uint256 _newICR) internal pure {
function _requireICRisAboveMCR(uint256 _newICR) internal view {
require(_newICR >= MCR, "BorrowerOps: An operation that would result in ICR < MCR is not permitted");
}

Expand Down
32 changes: 13 additions & 19 deletions contracts/src/CollSurplusPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

import "./Interfaces/ICollSurplusPool.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";

contract CollSurplusPool is Ownable, CheckContract, ICollSurplusPool {
contract CollSurplusPool is Ownable, ICollSurplusPool {
using SafeERC20 for IERC20;

string public constant NAME = "CollSurplusPool";
Expand All @@ -21,19 +20,18 @@ contract CollSurplusPool is Ownable, CheckContract, ICollSurplusPool {
// deposited ether tracker
uint256 internal ETHBalance;
// Collateral surplus claimable by trove owners
mapping(uint256 => uint256) internal balances;
mapping(address => uint256) internal balances;

// --- Events ---

event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolAddressChanged(address _newActivePoolAddress);

event CollBalanceUpdated(uint256 indexed _troveId, uint256 _newBalance);
event CollBalanceUpdated(address indexed _account, uint256 _newBalance);
event EtherSent(address _to, uint256 _amount);

constructor(address _ETHAddress) {
checkContract(_ETHAddress);
ETH = IERC20(_ETHAddress);
}

Expand All @@ -44,10 +42,6 @@ contract CollSurplusPool is Ownable, CheckContract, ICollSurplusPool {
override
onlyOwner
{
checkContract(_borrowerOperationsAddress);
checkContract(_troveManagerAddress);
checkContract(_activePoolAddress);

borrowerOperationsAddress = _borrowerOperationsAddress;
troveManagerAddress = _troveManagerAddress;
activePoolAddress = _activePoolAddress;
Expand All @@ -65,29 +59,29 @@ contract CollSurplusPool is Ownable, CheckContract, ICollSurplusPool {
return ETHBalance;
}

function getCollateral(uint256 _troveId) external view override returns (uint256) {
return balances[_troveId];
function getCollateral(address _account) external view override returns (uint256) {
return balances[_account];
}

// --- Pool functionality ---

function accountSurplus(uint256 _troveId, uint256 _amount) external override {
function accountSurplus(address _account, uint256 _amount) external override {
_requireCallerIsTroveManager();

uint256 newAmount = balances[_troveId] + _amount;
balances[_troveId] = newAmount;
uint256 newAmount = balances[_account] + _amount;
balances[_account] = newAmount;
ETHBalance = ETHBalance + _amount;

emit CollBalanceUpdated(_troveId, newAmount);
emit CollBalanceUpdated(_account, newAmount);
}

function claimColl(address _account, uint256 _troveId) external override {
function claimColl(address _account) external override {
_requireCallerIsBorrowerOperations();
uint256 claimableColl = balances[_troveId];
uint256 claimableColl = balances[_account];
require(claimableColl > 0, "CollSurplusPool: No collateral available to claim");

balances[_troveId] = 0;
emit CollBalanceUpdated(_troveId, 0);
balances[_account] = 0;
emit CollBalanceUpdated(_account, 0);

ETHBalance = ETHBalance - claimableColl;
emit EtherSent(_account, claimableColl);
Expand Down
11 changes: 6 additions & 5 deletions contracts/src/CollateralRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ contract CollateralRegistry is LiquityBase, ICollateralRegistry {
uint256 redeemedAmount;
}

function redeemCollateral(uint256 _boldAmount, uint256 _maxIterationsPerCollateral, uint256 _maxFeePercentage) external {
function redeemCollateral(uint256 _boldAmount, uint256 _maxIterationsPerCollateral, uint256 _maxFeePercentage)
external
{
_requireValidMaxFeePercentage(_maxFeePercentage);
_requireAmountGreaterThanZero(_boldAmount);
_requireBoldBalanceCoversRedemption(boldToken, msg.sender, _boldAmount);
Expand All @@ -135,7 +137,8 @@ contract CollateralRegistry is LiquityBase, ICollateralRegistry {
// We only compute it here, and update it at the end,
// because the final redeemed amount may be less than the requested amount
// Redeemers should take this into account in order to request the optimal amount to not overpay
uint256 redemptionRate = _calcRedemptionRate(_getUpdatedBaseRateFromRedemption(_boldAmount, totals.boldSupplyAtStart));
uint256 redemptionRate =
_calcRedemptionRate(_getUpdatedBaseRateFromRedemption(_boldAmount, totals.boldSupplyAtStart));
require(redemptionRate <= _maxFeePercentage, "CR: Fee exceeded provided maximum");
// Implicit by the above and the _requireValidMaxFeePercentage checks
//require(newBaseRate < DECIMAL_PRECISION, "CR: Fee would eat up all collateral");
Expand Down Expand Up @@ -195,9 +198,7 @@ contract CollateralRegistry is LiquityBase, ICollateralRegistry {
}

// Updates the `baseRate` state with math from `_getUpdatedBaseRateFromRedemption`
function _updateBaseRateAndGetRedemptionRate(uint256 _boldAmount, uint256 _totalBoldSupplyAtStart)
internal
{
function _updateBaseRateAndGetRedemptionRate(uint256 _boldAmount, uint256 _totalBoldSupplyAtStart) internal {
uint256 newBaseRate = _getUpdatedBaseRateFromRedemption(_boldAmount, _totalBoldSupplyAtStart);

//assert(newBaseRate <= DECIMAL_PRECISION); // This is already enforced in `_getUpdatedBaseRateFromRedemption`
Expand Down
3 changes: 0 additions & 3 deletions contracts/src/Dependencies/LiquityBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ contract LiquityBase is BaseMath, ILiquityBase {

uint256 public constant _100pct = 1000000000000000000; // 1e18 == 100%

// Minimum collateral ratio for individual troves
uint256 public constant MCR = 1100000000000000000; // 110%

// Critical system collateral ratio. If the system's total collateral ratio (TCR) falls below the CCR, Recovery Mode is triggered.
uint256 public constant CCR = 1500000000000000000; // 150%

Expand Down
4 changes: 4 additions & 0 deletions contracts/src/Dependencies/LiquityMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ library LiquityMath {
return (_a >= _b) ? _a : _b;
}

function _sub_min_0(uint256 _a, uint256 _b) internal pure returns (uint256) {
return (_a > _b) ? _a - _b : 0;
}

/*
* Multiply two decimal numbers and use normal rounding rules:
* -round product up if 19'th mantissa digit >= 5
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/HintHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ contract HintHelpers is LiquityBase, Ownable, CheckContract {
uint256 currentId = troveManager.getTroveFromTroveIdsArray(arrayIndex);

// Skip this Trove if it's unredeeamable and not in the sorted list
if (!sortedTroves.contains(currentId)) { continue; }
if (!sortedTroves.contains(currentId)) continue;

uint256 currentInterestRate = troveManager.getTroveAnnualInterestRate(currentId);

Expand Down
7 changes: 3 additions & 4 deletions contracts/src/Interfaces/IBorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ interface IBorrowerOperations is ILiquityBase {
function sortedTroves() external view returns (ISortedTroves);

function setAddresses(
address _troveManagerAddress,
address _activePoolAddress,
address _defaultPoolAddress,
address _stabilityPoolAddress,
Expand Down Expand Up @@ -57,7 +56,7 @@ interface IBorrowerOperations is ILiquityBase {
) external;

function adjustUnredeemableTrove(
uint256 _troveId,
uint256 _troveId,
uint256 _maxFeePercentage,
uint256 _collChange,
bool _isCollIncrease,
Expand All @@ -66,8 +65,8 @@ interface IBorrowerOperations is ILiquityBase {
uint256 _upperHint,
uint256 _lowerHint
) external;
function claimCollateral(uint256 _troveId) external;

function claimCollateral() external;

function setAddManager(uint256 _troveId, address _manager) external;
function setRemoveManager(uint256 _troveId, address _manager) external;
Expand Down
6 changes: 3 additions & 3 deletions contracts/src/Interfaces/ICollSurplusPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ interface ICollSurplusPool {

function getETHBalance() external view returns (uint256);

function getCollateral(uint256 _troveId) external view returns (uint256);
function getCollateral(address _account) external view returns (uint256);

function accountSurplus(uint256 _troveId, uint256 _amount) external;
function accountSurplus(address _account, uint256 _amount) external;

function claimColl(address _account, uint256 _troveId) external;
function claimColl(address _account) external;
}
1 change: 0 additions & 1 deletion contracts/src/Interfaces/ILiquityBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ interface ILiquityBase {
function priceFeed() external view returns (IPriceFeed);
function BOLD_GAS_COMPENSATION() external view returns (uint256);
function MIN_NET_DEBT() external view returns (uint256);
function MCR() external view returns (uint256);
function getEntireSystemDebt() external view returns (uint256);
}
2 changes: 2 additions & 0 deletions contracts/src/Interfaces/ITroveManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import "./ISortedTroves.sol";

// Common interface for the Trove Manager.
interface ITroveManager is IERC721, ILiquityBase {
function MCR() external view returns (uint256);

function setAddresses(
address _borrowerOperationsAddress,
address _activePoolAddress,
Expand Down
Loading