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

Apply and mint interest #76

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 93 additions & 12 deletions contracts/src/ActivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
pragma solidity 0.8.18;

import './Interfaces/IActivePool.sol';
import './Interfaces/IBoldToken.sol';
import "./Interfaces/IInterestRouter.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.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 @@ -20,8 +24,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 ETH; // 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 @@ -30,16 +57,19 @@ 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 _ETH);


// --- Contract setters ---

function setAddresses(
address _borrowerOperationsAddress,
address _troveManagerAddress,
address _stabilityPoolAddress,
address _defaultPoolAddress
address _defaultPoolAddress,
address _boldTokenAddress,
address _interestRouterAddress
)
external
onlyOwner
Expand All @@ -48,11 +78,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 @@ -73,8 +107,17 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
return ETH;
}

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 All @@ -86,19 +129,57 @@ contract ActivePool is Ownable, CheckContract, IActivePool {
emit EtherSent(_account, _amount);

(bool success, ) = _account.call{ value: _amount }("");
require(success, "ActivePool: sending ETH failed");
// require(success, "ActivePool: sending ETH failed");
}

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

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

recordedDebtSum = newRecordedDebtSum;

emit ActivePoolBoldDebtUpdated(newRecordedDebtSum);
}

function changeAggWeightedDebtSum(uint256 _oldWeightedRecordedTroveDebt, uint256 _newTroveWeightedRecordedTroveDebt) external {
_requireCallerIsBOorTroveM();
// 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
}

// --- 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 @@ -257,8 +263,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
Loading