Skip to content

Commit

Permalink
Fix individual ICR calc and add interest tests for withdrawETHGainToT…
Browse files Browse the repository at this point in the history
…rove
  • Loading branch information
RickGriff committed Feb 29, 2024
1 parent b3d85a6 commit 3dd8b70
Show file tree
Hide file tree
Showing 9 changed files with 537 additions and 26 deletions.
2 changes: 1 addition & 1 deletion contracts/src/ActivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ contract ActivePool is Ownable, CheckContract, IActivePool {

// 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 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),
Expand Down
17 changes: 8 additions & 9 deletions contracts/src/BorrowerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";

import "forge-std/console2.sol";

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

contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOperations {
string constant public NAME = "BorrowerOperations";
Expand Down Expand Up @@ -273,8 +272,8 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe

vars.price = priceFeed.fetchPrice();

uint256 initialWeightedRecordedTroveDebt = contractsCache.troveManager.getTroveWeightedRecordedDebt(msg.sender);
uint256 annualInterestRate = contractsCache.troveManager.getTroveAnnualInterestRate(msg.sender);
uint256 initialWeightedRecordedTroveDebt = contractsCache.troveManager.getTroveWeightedRecordedDebt(_borrower);
uint256 annualInterestRate = contractsCache.troveManager.getTroveAnnualInterestRate(_borrower);

// --- Checks ---

Expand All @@ -294,7 +293,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
// Get the collChange based on whether or not ETH was sent in the transaction
(vars.collChange, vars.isCollIncrease) = _getCollChange(msg.value, _collWithdrawal);

(vars.entireDebt, vars.entireColl, vars.redistDebtGain, , vars.accruedTroveInterest) = contractsCache.troveManager.getEntireDebtAndColl(msg.sender);
(vars.entireDebt, vars.entireColl, vars.redistDebtGain, , vars.accruedTroveInterest) = contractsCache.troveManager.getEntireDebtAndColl(_borrower);

// Get the trove's old ICR before the adjustment, and what its new ICR will be after the adjustment
vars.oldICR = LiquityMath._computeCR(vars.entireColl, vars.entireDebt, vars.price);
Expand Down Expand Up @@ -323,17 +322,17 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
contractsCache.troveManager.getAndApplyRedistributionGains(_borrower);

if (_isDebtIncrease) {
// Incresae Trove debt by the drawn debt + redist. gain
// 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 debt and coll
// Update the Trove's recorded coll and debt
vars.newEntireColl = _updateTroveCollFromAdjustment(contractsCache.troveManager, _borrower, vars.collChange, vars.isCollIncrease);
vars.newEntireDebt = _updateTroveDebtFromAdjustment(contractsCache.troveManager, _borrower, vars.entireDebt, _boldChange, _isDebtIncrease);

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

emit TroveUpdated(_borrower, vars.newEntireDebt, vars.newEntireColl, vars.stake, BorrowerOperation.adjustTrove);
Expand All @@ -342,7 +341,7 @@ contract BorrowerOperations is LiquityBase, Ownable, CheckContract, IBorrowerOpe
_moveTokensAndETHfromAdjustment(
contractsCache.activePool,
contractsCache.boldToken,
msg.sender,
_borrower,
vars.collChange,
vars.isCollIncrease,
_boldChange,
Expand Down
1 change: 1 addition & 0 deletions contracts/src/Interfaces/ILiquityBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ interface ILiquityBase {
function defaultPool() external view returns (IDefaultPool);
function priceFeed() external view returns (IPriceFeed);
function BOLD_GAS_COMPENSATION() external view returns (uint256);
function MCR() external view returns (uint256);
function getEntireSystemDebt() external view returns (uint256);
}
2 changes: 2 additions & 0 deletions contracts/src/StabilityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";

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

/*
* The Stability Pool holds Bold tokens deposited by Stability Pool depositors.
*
Expand Down
6 changes: 4 additions & 2 deletions contracts/src/TroveManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";

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

contract TroveManager is LiquityBase, ITroveManager, Ownable, CheckContract {
string constant public NAME = "TroveManager";
Expand Down Expand Up @@ -987,8 +987,10 @@ contract TroveManager is LiquityBase, ITroveManager, Ownable, CheckContract {
uint pendingETHReward = getPendingETHReward(_borrower);
uint pendingBoldDebtReward = getPendingBoldDebtReward(_borrower);

uint256 accruedTroveInterest = calcTroveAccruedInterest(_borrower);

uint currentETH = Troves[_borrower].coll + pendingETHReward;
uint currentBoldDebt = Troves[_borrower].debt + pendingBoldDebtReward;
uint currentBoldDebt = Troves[_borrower].debt + pendingBoldDebtReward + accruedTroveInterest;

return (currentETH, currentBoldDebt);
}
Expand Down
25 changes: 25 additions & 0 deletions contracts/src/test/TestContracts/BaseTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ contract BaseTest is Test {
vm.stopPrank();
}

function liquidate(address _from, address _borrower) public {
vm.startPrank(_from);
troveManager.liquidate(_borrower);
vm.stopPrank();
}

function withdrawETHGainToTrove(address _from) public {
vm.startPrank(_from);
stabilityPool.withdrawETHGainToTrove();
vm.stopPrank();
}

function logContractAddresses() public view {
console.log("ActivePool addr: ", address(activePool));
console.log("BorrowerOps addr: ", address(borrowerOperations));
Expand All @@ -160,4 +172,17 @@ contract BaseTest is Test {
console.log("TroveManager addr: ", address(troveManager));
console.log("BoldToken addr: ", address(boldToken));
}

function abs(uint256 x, uint256 y) public pure returns (uint256) {
return x > y ? x - y : y - x;
}

function assertApproximatelyEqual(uint256 _x, uint256 _y, uint256 _margin) public {
assertApproximatelyEqual(_x, _y, _margin, "");
}

function assertApproximatelyEqual(uint256 _x, uint256 _y, uint256 _margin, string memory _reason) public {
uint256 diff = abs(_x, _y);
assertLe(diff, _margin, _reason);
}
}
37 changes: 37 additions & 0 deletions contracts/src/test/TestContracts/DevTestSetup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,41 @@ contract DevTestSetup is BaseTest {
address(activePool)
);
}

function _setupForWithdrawETHGainToTrove() internal {
uint256 troveDebtRequest_A = 2000e18;
uint256 troveDebtRequest_B = 3000e18;
uint256 troveDebtRequest_C = 4500e18;
uint256 interestRate = 5e16; // 5%

uint256 price = 2000e18;
priceFeed.setPrice(price);

openTroveNoHints100pctMaxFee(A, 5 ether, troveDebtRequest_A, interestRate);
openTroveNoHints100pctMaxFee(B, 5 ether, troveDebtRequest_B, interestRate);
openTroveNoHints100pctMaxFee(C, 5 ether, troveDebtRequest_C, interestRate);

console.log(troveManager.getTCR(price), "TCR");
console.log(troveManager.getCurrentICR(C, price), "C CR");

// A and B deposit to SP
makeSPDeposit(A, troveDebtRequest_A);
makeSPDeposit(B, troveDebtRequest_B);

// Price drops, C becomes liquidateable
price = 1025e18;
priceFeed.setPrice(price);

console.log(troveManager.getTCR(price), "TCR before liq");
console.log(troveManager.getCurrentICR(C, price), "C CR before liq");

assertFalse(troveManager.checkRecoveryMode(price));
assertLt(troveManager.getCurrentICR(C, price), troveManager.MCR());

// A liquidates C
liquidate(A, C);

// check A has an ETH gain
assertGt(stabilityPool.getDepositorETHGain(A), 0);
}
}
Loading

0 comments on commit 3dd8b70

Please sign in to comment.