Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into batch-delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
danielattilasimon committed Apr 11, 2024
2 parents 464cb76 + 17ea02e commit 0a89944
Show file tree
Hide file tree
Showing 17 changed files with 659 additions and 251 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
id: test

deploy:
if: false
name: Deploy contracts to Liquity v2 Testnet
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -128,6 +129,7 @@ jobs:
run: pnpm test

coverage:
if: false
name: Coverage
runs-on: ubuntu-latest
continue-on-error: true
Expand Down
68 changes: 48 additions & 20 deletions contracts/src/ActivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -173,24 +173,26 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
emit ActivePoolETHBalanceUpdated(newETHBalance);
}

function increaseRecordedDebtSum(uint _amount) external override {
_requireCallerIsBOorTroveM();
uint256 newRecordedDebtSum = recordedDebtSum + _amount;
recordedDebtSum = newRecordedDebtSum;
emit ActivePoolBoldDebtUpdated(newRecordedDebtSum);
function increaseRecordedDebtSum(uint256 _amount) external {
_requireCallerIsTroveManager();
_changeRecordedDebtSum(_amount, 0);
}

function decreaseRecordedDebtSum(uint _amount) external override {
_requireCallerIsBOorTroveMorSP();
uint256 newRecordedDebtSum = recordedDebtSum - _amount;

recordedDebtSum = newRecordedDebtSum;
// TODO: remove this once we implement interest minting in redemptions
function decreaseRecordedDebtSum(uint256 _amount) external {
_requireCallerIsTroveManager();
_changeRecordedDebtSum(0, _amount);
}

function _changeRecordedDebtSum(uint256 _recordedDebtIncrease, uint256 _recordedDebtDecrease) internal {
// Do the arithmetic in 2 steps here to avoid overflow from the decrease
uint256 newRecordedDebtSum = recordedDebtSum + _recordedDebtIncrease; // 1 SLOAD
newRecordedDebtSum -= _recordedDebtDecrease;
recordedDebtSum = newRecordedDebtSum; // 1 SSTORE
emit ActivePoolBoldDebtUpdated(newRecordedDebtSum);
}

function changeAggWeightedDebtSum(uint256 _oldWeightedRecordedTroveDebt, uint256 _newTroveWeightedRecordedTroveDebt) external {
_requireCallerIsBOorTroveM();
function _changeAggWeightedDebtSum( uint256 _newTroveWeightedRecordedTroveDebt, uint256 _oldWeightedRecordedTroveDebt) internal {
// Do the arithmetic in 2 steps here to avoid overflow from the decrease
uint256 newAggWeightedDebtSum = aggWeightedDebtSum + _newTroveWeightedRecordedTroveDebt; // 1 SLOAD
newAggWeightedDebtSum -= _oldWeightedRecordedTroveDebt;
Expand All @@ -206,21 +208,42 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
// It does *not* include the Trove's individual accrued interest - this gets accounted for in the aggregate accrued interest.
// The net Trove debt change could be positive or negative in a repayment (depending on whether its redistribution gain or repayment amount is larger),
// so this function accepts both the increase and the decrease to avoid using (and converting to/from) signed ints.
function mintAggInterest(uint256 _troveDebtIncrease, uint256 _troveDebtDecrease) public {
_requireCallerIsBOorTroveMorSP();
uint256 aggInterest = calcPendingAggInterest();
// Mint the new BOLD interest to a mock interest router that would split it and send it onward to SP, LP staking, etc.
// TODO: implement interest routing and SP Bold reward tracking
if (aggInterest > 0) {boldToken.mint(address(interestRouter), aggInterest);}

function mintAggInterest(
uint256 _troveDebtIncrease,
uint256 _troveDebtDecrease,
uint256 recordedSumIncrease,
uint256 recordedSumDecrease,
uint256 newWeightedRecordedTroveDebt,
uint256 oldWeightedRecordedTroveDebt
)
external
{
_requireCallerIsBOorTroveM();

// Do the arithmetic in 2 steps here to avoid overflow from the decrease
uint256 newAggRecordedDebt = aggRecordedDebt + aggInterest + _troveDebtIncrease; // 1 SLOAD
uint256 newAggRecordedDebt = _mintAggInterestNoTroveChange() + _troveDebtIncrease; // 1 SLOAD
newAggRecordedDebt -=_troveDebtDecrease;
aggRecordedDebt = newAggRecordedDebt; // 1 SSTORE
// assert(aggRecordedDebt >= 0) // This should never be negative. If all redistribution gians and all aggregate interest was applied
// and all Trove debts were repaid, it should become 0.

_changeRecordedDebtSum(recordedSumIncrease, recordedSumDecrease);
_changeAggWeightedDebtSum(newWeightedRecordedTroveDebt, oldWeightedRecordedTroveDebt);
}

function mintAggInterestNoTroveChange() external returns (uint256) {
_requireCallerIsSP();
aggRecordedDebt = _mintAggInterestNoTroveChange();
}

function _mintAggInterestNoTroveChange() internal returns (uint256) {
uint256 aggInterest = calcPendingAggInterest();
// Mint the new BOLD interest to a mock interest router that would split it and send it onward to SP, LP staking, etc.
// TODO: implement interest routing and SP Bold reward tracking
if (aggInterest > 0) {boldToken.mint(address(interestRouter), aggInterest);}

lastAggUpdateTime = block.timestamp;
return aggRecordedDebt + aggInterest;
}

// --- 'require' functions ---
Expand All @@ -240,6 +263,11 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
"ActivePool: Caller is neither BorrowerOperations nor TroveManager nor StabilityPool");
}

function _requireCallerIsSP() internal view {
require(
msg.sender == stabilityPoolAddress, "ActivePool: Caller is not StabilityPool");
}

function _requireCallerIsBOorTroveM() internal view {
require(
msg.sender == borrowerOperationsAddress ||
Expand Down
101 changes: 52 additions & 49 deletions contracts/src/BorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
uint newEntireDebt;
uint newEntireColl;
uint stake;
uint256 initialWeightedRecordedTroveDebt;
uint256 newWeightedTroveDebt;
uint256 annualInterestRate;
uint256 troveDebtIncrease;
uint256 troveDebtDecrease;
uint256 recordedDebtIncrease;
uint256 recordedDebtDecrease;
}

struct LocalVariables_openTrove {
Expand Down Expand Up @@ -202,7 +209,8 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

// --- Effects & interactions ---

contractsCache.activePool.mintAggInterest(vars.compositeDebt, 0);
uint256 weightedRecordedTroveDebt = vars.compositeDebt * _annualInterestRate;
contractsCache.activePool.mintAggInterest(vars.compositeDebt, 0, vars.compositeDebt, 0, weightedRecordedTroveDebt, 0);

// Set the stored Trove properties and mint the NFT
vars.stake = contractsCache.troveManager.setTrovePropertiesOnOpen(
Expand All @@ -224,11 +232,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
contractsCache.boldToken.mint(msg.sender, _boldAmount);
contractsCache.boldToken.mint(gasPoolAddress, BOLD_GAS_COMPENSATION);

// Add the whole debt to the recorded debt tracker
contractsCache.activePool.increaseRecordedDebtSum(vars.compositeDebt);
// Add the whole weighted debt to the weighted recorded debt tracker
contractsCache.activePool.changeAggWeightedDebtSum(0, vars.compositeDebt * _annualInterestRate);

emit TroveUpdated(troveId, vars.compositeDebt, _ETHAmount, vars.stake, BorrowerOperation.openTrove);
emit BoldBorrowingFeePaid(troveId, vars.BoldFee); // TODO

Expand Down Expand Up @@ -315,8 +318,8 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

vars.price = priceFeed.fetchPrice();

uint256 initialWeightedRecordedTroveDebt = contractsCache.troveManager.getTroveWeightedRecordedDebt(_troveId);
uint256 annualInterestRate = contractsCache.troveManager.getTroveAnnualInterestRate(_troveId);
vars.initialWeightedRecordedTroveDebt = contractsCache.troveManager.getTroveWeightedRecordedDebt(_troveId);
vars.annualInterestRate = contractsCache.troveManager.getTroveAnnualInterestRate(_troveId);

// --- Checks ---

Expand Down Expand Up @@ -364,14 +367,6 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

contractsCache.troveManager.getAndApplyRedistributionGains(_troveId);

if (_isDebtIncrease) {
// Increase Trove debt by the drawn debt + redist. gain
activePool.mintAggInterest(_boldChange + vars.redistDebtGain, 0);
} else {
// Increase Trove debt by redist. gain and decrease by the repaid debt
activePool.mintAggInterest(vars.redistDebtGain, _boldChange);
}

// Update the Trove's recorded coll and debt
vars.newEntireColl = _updateTroveCollFromAdjustment(
contractsCache.troveManager,
Expand All @@ -393,6 +388,30 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

vars.stake = contractsCache.troveManager.updateStakeAndTotalStakes(_troveId);

vars.newWeightedTroveDebt = vars.newEntireDebt * vars.annualInterestRate;

if (_isDebtIncrease) {
// Increase Trove debt by the drawn debt + redist. gain
vars.troveDebtIncrease = _boldChange + vars.redistDebtGain;
vars.recordedDebtIncrease = _boldChange + vars.accruedTroveInterest;
} else {
// Increase Trove debt by redist. gain and decrease by the repaid debt
vars.troveDebtIncrease = vars.redistDebtGain;
vars.troveDebtDecrease = _boldChange;

vars.recordedDebtIncrease = vars.accruedTroveInterest;
vars.recordedDebtDecrease = _boldChange;
}

activePool.mintAggInterest(
vars.troveDebtIncrease,
vars.troveDebtDecrease,
vars.recordedDebtIncrease,
vars.recordedDebtDecrease,
vars.newWeightedTroveDebt,
vars.initialWeightedRecordedTroveDebt
);

emit TroveUpdated(_troveId, vars.newEntireDebt, vars.newEntireColl, vars.stake, BorrowerOperation.adjustTrove);
emit BoldBorrowingFeePaid(_troveId, vars.BoldFee); // TODO

Expand All @@ -404,11 +423,8 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
_collChange,
_isCollIncrease,
_boldChange,
_isDebtIncrease,
vars.accruedTroveInterest
_isDebtIncrease
);

contractsCache.activePool.changeAggWeightedDebtSum(initialWeightedRecordedTroveDebt, vars.newEntireDebt * annualInterestRate);
}

function closeTrove(uint256 _troveId) external override {
Expand Down Expand Up @@ -445,19 +461,17 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

// Remove the Trove's initial recorded debt plus its accrued interest from ActivePool.aggRecordedDebt,
// but *don't* remove the redistribution gains, since these were not yet incorporated into the sum.
contractsCache.activePool.mintAggInterest(0, initialRecordedTroveDebt + accruedTroveInterest);

contractsCache.troveManager.removeStake(_troveId);
contractsCache.troveManager.closeTrove(_troveId);
emit TroveUpdated(_troveId, 0, 0, 0, BorrowerOperation.closeTrove);

uint256 troveDebtDecrease = initialRecordedTroveDebt + accruedTroveInterest;
// Remove only the Trove's latest recorded debt (inc. redist. gains) from the recorded debt tracker,
// i.e. exclude the accrued interest since it has not been added.
// TODO: If/when redist. gains are gas-optimized, exclude them from here too.
contractsCache.activePool.decreaseRecordedDebtSum(initialRecordedTroveDebt + debtRedistGain);
uint256 recordedDebtSumDecrease = initialRecordedTroveDebt + debtRedistGain;

contractsCache.activePool.mintAggInterest(0, troveDebtDecrease, 0, recordedDebtSumDecrease, 0, initialWeightedRecordedTroveDebt);

// Remove Trove's weighted debt from the weighted sum
activePool.changeAggWeightedDebtSum(initialWeightedRecordedTroveDebt, 0);
contractsCache.troveManager.removeStake(_troveId);
contractsCache.troveManager.closeTrove(_troveId);
emit TroveUpdated(_troveId, 0, 0, 0, BorrowerOperation.closeTrove);

// Burn the 200 BOLD gas compensation
contractsCache.boldToken.burn(gasPoolAddress, BOLD_GAS_COMPENSATION);
Expand All @@ -478,7 +492,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

uint256 entireTroveDebt = _updateActivePoolTrackersNoDebtChange(contractsCache.troveManager, contractsCache.activePool, _troveId, annualInterestRate);

// Update Trove recorded debt and interest-weighted debt sum
// Update Trove recorded debt
contractsCache.troveManager.updateTroveDebtFromInterestApplication(_troveId, entireTroveDebt);
}

Expand Down Expand Up @@ -578,9 +592,8 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
return newEntireDebt;
}

// This function incorporates both the Trove's net debt change (repaid/drawn) and its accrued interest.
// Redist. gains have already been applied before this is called.
// TODO: explicitly pass redist. gains too if we gas-optimize them.
// This function mints the BOLD corresponding to the borrower's chosen debt increase
// (it does not mint the accrued interest).
function _moveTokensAndETHfromAdjustment
(
IActivePool _activePool,
Expand All @@ -590,20 +603,14 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
uint _collChange,
bool _isCollIncrease,
uint _boldChange,
bool _isDebtIncrease,
uint256 _accruedTroveInterest
bool _isDebtIncrease
)
internal
{
if (_isDebtIncrease) {
_activePool.increaseRecordedDebtSum(_boldChange + _accruedTroveInterest);
address borrower = _troveManager.ownerOf(_troveId);
_boldToken.mint(borrower, _boldChange);
} else {
// TODO: Gas optimize this
_activePool.increaseRecordedDebtSum(_accruedTroveInterest);
_activePool.decreaseRecordedDebtSum(_boldChange);

_boldToken.burn(msg.sender, _boldChange);
}

Expand Down Expand Up @@ -639,19 +646,15 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

(, uint256 redistDebtGain) = _troveManager.getAndApplyRedistributionGains(_troveId);

// No debt is issued/repaid, so the net Trove debt change is purely the redistribution gain
_activePool.mintAggInterest(redistDebtGain, 0);

uint256 accruedTroveInterest = _troveManager.calcTroveAccruedInterest(_troveId);
uint256 recordedTroveDebt = _troveManager.getTroveDebt(_troveId);
uint256 entireTroveDebt = recordedTroveDebt + accruedTroveInterest;

uint256 newWeightedTroveDebt = entireTroveDebt * _annualInterestRate;
// Add only the Trove's accrued interest to the recorded debt tracker since we have already applied redist. gains.
// TODO: include redist. gains here if we gas-optimize them
_activePool.increaseRecordedDebtSum(accruedTroveInterest);
// Remove the old weighted recorded debt and and add the new one to the relevant tracker
_activePool.changeAggWeightedDebtSum(initialWeightedRecordedTroveDebt, entireTroveDebt * _annualInterestRate);

// No debt is issued/repaid, so the net Trove debt change is purely the redistribution gain
// TODO: also include redist. gains here in the recordedSumIncrease arg if we gas-optimize them
_activePool.mintAggInterest(redistDebtGain, 0, accruedTroveInterest, 0, newWeightedTroveDebt, initialWeightedRecordedTroveDebt);

return entireTroveDebt;
}

Expand Down
13 changes: 9 additions & 4 deletions contracts/src/Interfaces/IActivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,16 @@ interface IActivePool {
function aggWeightedDebtSum() external view returns (uint256);
function calcPendingAggInterest() external view returns (uint256);

function mintAggInterest(uint256 _troveDebtIncrease, uint256 _troveDebtDecrease) external;
function changeAggWeightedDebtSum(
uint256 _oldWeightedRecordedTroveDebt,
uint256 _newTroveWeightedRecordedTroveDebt
function mintAggInterest(
uint256 _troveDebtIncrease,
uint256 _troveDebtDecrease,
uint256 recordedSumIncrease,
uint256 recordedSumDecrease,
uint256 newWeightedRecordedTroveDebt,
uint256 oldWeightedRecordedTroveDebt
) external;

function mintAggInterestNoTroveChange() external returns (uint256);
function increaseRecordedDebtSum(uint256 _amount) external;
function decreaseRecordedDebtSum(uint256 _amount) external;
function sendETH(address _account, uint _amount) external;
Expand Down
4 changes: 2 additions & 2 deletions contracts/src/StabilityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool {
function provideToSP(uint _amount) external override {
_requireNonZeroAmount(_amount);

activePool.mintAggInterest(0, 0);
activePool.mintAggInterestNoTroveChange();

uint initialDeposit = deposits[msg.sender].initialValue;

Expand Down Expand Up @@ -318,7 +318,7 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool {
uint initialDeposit = deposits[msg.sender].initialValue;
_requireUserHasDeposit(initialDeposit);

activePool.mintAggInterest(0, 0);
activePool.mintAggInterestNoTroveChange();

uint depositorETHGain = getDepositorETHGain(msg.sender);

Expand Down
Loading

0 comments on commit 0a89944

Please sign in to comment.