Skip to content

Commit

Permalink
Merge pull request #85 from liquity/apply_and_mint_interest_rebase
Browse files Browse the repository at this point in the history
Apply and mint interest
  • Loading branch information
bingen committed Apr 4, 2024
2 parents 5c9f2f6 + 3942a9d commit a3e5d0c
Show file tree
Hide file tree
Showing 39 changed files with 4,342 additions and 726 deletions.
104 changes: 92 additions & 12 deletions contracts/src/ActivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ pragma solidity 0.8.18;

import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

import './Interfaces/IActivePool.sol';
import './Interfaces/IBoldToken.sol';
import "./Interfaces/IInterestRouter.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";
import './Interfaces/IDefaultPool.sol';
import './Interfaces/IActivePool.sol';

// import "forge-std/console.sol";
//import "forge-std/console2.sol";

/*
* The Active Pool holds the ETH collateral and Bold debt (but not Bold tokens) for all active troves.
Expand All @@ -28,8 +31,31 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
address public troveManagerAddress;
address public stabilityPoolAddress;
address public defaultPoolAddress;

IBoldToken boldToken;

IInterestRouter public interestRouter;

uint256 constant public SECONDS_IN_ONE_YEAR = 31536000; // 60 * 60 * 24 * 365,

uint256 internal ETHBalance; // deposited ether tracker
uint256 internal boldDebt;

// Sum of individual recorded Trove debts. Updated only at individual Trove operations.
// "G" in the spec.
uint256 internal recordedDebtSum;

// Aggregate recorded debt tracker. Updated whenever a Trove's debt is touched AND whenever the aggregate pending interest is minted.
// "D" in the spec.
uint256 public aggRecordedDebt;

/* Sum of individual recorded Trove debts weighted by their respective chosen interest rates.
* Updated at individual Trove operations.
* "S" in the spec.
*/
uint256 public aggWeightedDebtSum;

// Last time at which the aggregate recorded debt and weighted sum were updated
uint256 public lastAggUpdateTime;

// --- Events ---

Expand All @@ -38,21 +64,24 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
event EtherSent(address _to, uint _amount);
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolBoldDebtUpdated(uint _boldDebt);
event ActivePoolBoldDebtUpdated(uint _recordedDebtSum);
event ActivePoolETHBalanceUpdated(uint _ETHBalance);

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


// --- Contract setters ---

function setAddresses(
address _borrowerOperationsAddress,
address _troveManagerAddress,
address _stabilityPoolAddress,
address _defaultPoolAddress
address _defaultPoolAddress,
address _boldTokenAddress,
address _interestRouterAddress
)
external
onlyOwner
Expand All @@ -61,11 +90,15 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
checkContract(_troveManagerAddress);
checkContract(_stabilityPoolAddress);
checkContract(_defaultPoolAddress);
checkContract(_boldTokenAddress);
checkContract(_interestRouterAddress);

borrowerOperationsAddress = _borrowerOperationsAddress;
troveManagerAddress = _troveManagerAddress;
stabilityPoolAddress = _stabilityPoolAddress;
defaultPoolAddress = _defaultPoolAddress;
boldToken = IBoldToken(_boldTokenAddress);
interestRouter = IInterestRouter(_interestRouterAddress);

emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress);
emit TroveManagerAddressChanged(_troveManagerAddress);
Expand All @@ -89,8 +122,17 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
return ETHBalance;
}

function getBoldDebt() external view override returns (uint) {
return boldDebt;
function getRecordedDebtSum() external view override returns (uint) {
return recordedDebtSum;
}

function calcPendingAggInterest() public view returns (uint256) {
return aggWeightedDebtSum * (block.timestamp - lastAggUpdateTime) / SECONDS_IN_ONE_YEAR / 1e18;
}

// Returns sum of agg.recorded debt plus agg. pending interest. Excludes pending redist. gains.
function getTotalActiveDebt() public view returns (uint256) {
return aggRecordedDebt + calcPendingAggInterest();
}

// --- Pool functionality ---
Expand Down Expand Up @@ -131,16 +173,54 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
emit ActivePoolETHBalanceUpdated(newETHBalance);
}

function increaseBoldDebt(uint _amount) external override {
function increaseRecordedDebtSum(uint _amount) external override {
_requireCallerIsBOorTroveM();
uint256 newRecordedDebtSum = recordedDebtSum + _amount;
recordedDebtSum = newRecordedDebtSum;
emit ActivePoolBoldDebtUpdated(newRecordedDebtSum);
}

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

recordedDebtSum = newRecordedDebtSum;

emit ActivePoolBoldDebtUpdated(newRecordedDebtSum);
}

function changeAggWeightedDebtSum(uint256 _oldWeightedRecordedTroveDebt, uint256 _newTroveWeightedRecordedTroveDebt) external {
_requireCallerIsBOorTroveM();
boldDebt = boldDebt + _amount;
emit ActivePoolBoldDebtUpdated(boldDebt);
// Do the arithmetic in 2 steps here to avoid overflow from the decrease
uint256 newAggWeightedDebtSum = aggWeightedDebtSum + _newTroveWeightedRecordedTroveDebt; // 1 SLOAD
newAggWeightedDebtSum -= _oldWeightedRecordedTroveDebt;
aggWeightedDebtSum = newAggWeightedDebtSum; // 1 SSTORE
}

function decreaseBoldDebt(uint _amount) external override {
// --- Aggregate interest operations ---

// This function is called inside all state-changing user ops: borrower ops, liquidations, redemptions and SP deposits/withdrawals.
// Some user ops trigger debt changes to Trove(s), in which case _troveDebtChange will be non-zero.
// The aggregate recorded debt is incremented by the aggregate pending interest, plus the net Trove debt change.
// The net Trove debt change consists of the sum of a) any debt issued/repaid and b) any redistribution debt gain applied in the encapsulating operation.
// 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();
boldDebt = boldDebt - _amount;
emit ActivePoolBoldDebtUpdated(boldDebt);
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);}

// Do the arithmetic in 2 steps here to avoid overflow from the decrease
uint256 newAggRecordedDebt = aggRecordedDebt + aggInterest + _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.

lastAggUpdateTime = block.timestamp;
}

// --- 'require' functions ---
Expand Down
16 changes: 12 additions & 4 deletions contracts/src/BoldToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ contract BoldToken is CheckContract, IBoldToken {
address public immutable troveManagerAddress;
address public immutable stabilityPoolAddress;
address public immutable borrowerOperationsAddress;
address public immutable activePoolAddress;


// --- Events ---
event TroveManagerAddressChanged(address _troveManagerAddress);
event StabilityPoolAddressChanged(address _newStabilityPoolAddress);
Expand All @@ -66,11 +68,13 @@ contract BoldToken is CheckContract, IBoldToken {
(
address _troveManagerAddress,
address _stabilityPoolAddress,
address _borrowerOperationsAddress
address _borrowerOperationsAddress,
address _activePoolAddress
) {
checkContract(_troveManagerAddress);
checkContract(_stabilityPoolAddress);
checkContract(_borrowerOperationsAddress);
checkContract(_activePoolAddress);

troveManagerAddress = _troveManagerAddress;
emit TroveManagerAddressChanged(_troveManagerAddress);
Expand All @@ -80,6 +84,8 @@ contract BoldToken is CheckContract, IBoldToken {

borrowerOperationsAddress = _borrowerOperationsAddress;
emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress);

activePoolAddress = _activePoolAddress;

bytes32 hashedName = keccak256(bytes(_NAME));
bytes32 hashedVersion = keccak256(bytes(_VERSION));
Expand All @@ -95,7 +101,7 @@ contract BoldToken is CheckContract, IBoldToken {
// --- Functions for intra-Liquity calls ---

function mint(address _account, uint256 _amount) external override {
_requireCallerIsBorrowerOperations();
_requireCallerIsBOorAP();
_mint(_account, _amount);
}

Expand Down Expand Up @@ -258,8 +264,10 @@ contract BoldToken is CheckContract, IBoldToken {
);
}

function _requireCallerIsBorrowerOperations() internal view {
require(msg.sender == borrowerOperationsAddress, "BoldToken: Caller is not BorrowerOperations");
function _requireCallerIsBOorAP() internal view {
require(msg.sender == borrowerOperationsAddress ||
msg.sender == activePoolAddress,
"BoldToken: Caller is not BO or AP");
}

function _requireCallerIsBOorTroveMorSP() internal view {
Expand Down
Loading

0 comments on commit a3e5d0c

Please sign in to comment.