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

feat: Add initial redemption base rate #110

Merged
merged 2 commits into from
Apr 11, 2024
Merged
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
4 changes: 2 additions & 2 deletions contracts/src/Interfaces/ITroveManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ interface ITroveManager is IERC721, ILiquityBase {
function sortedTroves() external view returns(ISortedTroves);
function borrowerOperationsAddress() external view returns (address);

function BOOTSTRAP_PERIOD() external view returns (uint256);

// function BOLD_GAS_COMPENSATION() external view returns (uint256);

function baseRate() external view returns (uint256);

function getTroveIdsCount() external view returns (uint);

function getTroveFromTroveIdsArray(uint _index) external view returns (uint256);
Expand Down
43 changes: 19 additions & 24 deletions contracts/src/TroveManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ contract TroveManager is ERC721, LiquityBase, Ownable, CheckContract, ITroveMana
*/
uint constant public MINUTE_DECAY_FACTOR = 999037758833783000;
uint constant public REDEMPTION_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5; // 0.5%
uint constant public MAX_BORROWING_FEE = DECIMAL_PRECISION / 100 * 5; // 5%
// To prevent redemptions unless Bold depegs below 0.95 and allow the system to take off
uint constant public INITIAL_REDEMPTION_RATE = DECIMAL_PRECISION / 100 * 5; // 5%

// During bootsrap period redemptions are not allowed
uint constant public BOOTSTRAP_PERIOD = 14 days;

/*
* BETA: 18 digit decimal. Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a redemption.
Expand Down Expand Up @@ -258,14 +257,19 @@ contract TroveManager is ERC721, LiquityBase, Ownable, CheckContract, ITroveMana
event TroveSnapshotsUpdated(uint _L_ETH, uint _L_boldDebt);
event TroveIndexUpdated(uint256 _troveId, uint _newIndex);

enum TroveManagerOperation {
enum TroveManagerOperation {
getAndApplyRedistributionGains,
liquidateInNormalMode,
liquidateInRecoveryMode,
redeemCollateral
}

constructor() ERC721(NAME, SYMBOL) {}
constructor() ERC721(NAME, SYMBOL) {
// Update the baseRate state variable
// To prevent redemptions unless Bold depegs below 0.95 and allow the system to take off
baseRate = INITIAL_REDEMPTION_RATE;
Copy link
Collaborator

@RickGriff RickGriff Apr 11, 2024

Choose a reason for hiding this comment

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

Since it still decays with the same half-life, in ~4 days it will be down to 0.2%. I wonder if this decrease is too rapid? We may want at least 1-2 weeks for TVL to build up.

Also, we've commented out redemption fees (see _calcRedemptionRate) so in practice no redemption fee will be charged until we change that.

emit BaseRateUpdated(INITIAL_REDEMPTION_RATE);
}

// --- Dependency setter ---

Expand Down Expand Up @@ -919,7 +923,6 @@ contract TroveManager is ERC721, LiquityBase, Ownable, CheckContract, ITroveMana
RedemptionTotals memory totals;

_requireValidMaxFeePercentage(_maxFeePercentage);
_requireAfterBootstrapPeriod();
totals.price = priceFeed.fetchPrice();
_requireTCRoverMCR(totals.price);
_requireAmountGreaterThanZero(_boldamount);
Expand Down Expand Up @@ -1364,16 +1367,14 @@ contract TroveManager is ERC721, LiquityBase, Ownable, CheckContract, ITroveMana
}

function getRedemptionRateWithDecay() public view override returns (uint) {
return 0;
// return _calcRedemptionRate(_calcDecayedBaseRate());
return _calcRedemptionRate(_calcDecayedBaseRate());
}

function _calcRedemptionRate(uint /* _baseRate */) internal pure returns (uint) {
return 0;
// return LiquityMath._min(
// REDEMPTION_FEE_FLOOR + _baseRate,
// DECIMAL_PRECISION // cap at a maximum of 100%
// );
function _calcRedemptionRate(uint _baseRate) internal pure returns (uint) {
return LiquityMath._min(
REDEMPTION_FEE_FLOOR + _baseRate,
DECIMAL_PRECISION // cap at a maximum of 100%
);
}

function _getRedemptionFee(uint _ETHDrawn) internal view returns (uint) {
Expand All @@ -1384,11 +1385,10 @@ contract TroveManager is ERC721, LiquityBase, Ownable, CheckContract, ITroveMana
return _calcRedemptionFee(getRedemptionRateWithDecay(), _ETHDrawn);
}

function _calcRedemptionFee(uint /* _redemptionRate */, uint /* _ETHDrawn */) internal pure returns (uint) {
return 0;
// uint redemptionFee = _redemptionRate * _ETHDrawn / DECIMAL_PRECISION;
// require(redemptionFee < _ETHDrawn, "TroveManager: Fee would eat up all returned collateral");
// return redemptionFee;
function _calcRedemptionFee(uint _redemptionRate, uint _ETHDrawn) internal pure returns (uint) {
uint redemptionFee = _redemptionRate * _ETHDrawn / DECIMAL_PRECISION;
require(redemptionFee < _ETHDrawn, "TroveManager: Fee would eat up all returned collateral");
return redemptionFee;
}

// --- Internal fee functions ---
Expand Down Expand Up @@ -1471,11 +1471,6 @@ contract TroveManager is ERC721, LiquityBase, Ownable, CheckContract, ITroveMana
require(_getTCR(_price) >= MCR, "TroveManager: Cannot redeem when TCR < MCR");
}

function _requireAfterBootstrapPeriod() internal view {
uint systemDeploymentTime = boldToken.deploymentStartTime();
require(block.timestamp >= systemDeploymentTime + BOOTSTRAP_PERIOD, "TroveManager: Redemptions are not allowed during bootstrap phase");
}

function _requireValidMaxFeePercentage(uint _maxFeePercentage) internal pure {
require(_maxFeePercentage >= REDEMPTION_FEE_FLOOR && _maxFeePercentage <= DECIMAL_PRECISION,
"Max fee percentage must be between 0.5% and 100%");
Expand Down
10 changes: 4 additions & 6 deletions contracts/src/test/basicOps.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ contract BasicOps is DevTestSetup {

uint256 trovesCount = troveManager.getTroveIdsCount();
assertEq(trovesCount, 2);

vm.startPrank(B);
borrowerOperations.closeTrove(B_Id);
vm.stopPrank();

// Check Troves count reduced by 1
trovesCount = troveManager.getTroveIdsCount();
assertEq(trovesCount, 1);
Expand All @@ -66,7 +66,7 @@ contract BasicOps is DevTestSetup {
assertGt(debt_1, 0);
uint256 coll_1 = troveManager.getTroveColl(A_Id);
assertGt(coll_1, 0);

// Adjust trove
borrowerOperations.adjustTrove(A_Id, 1e18, 1e18, true, 500e18, true);

Expand Down Expand Up @@ -94,8 +94,6 @@ contract BasicOps is DevTestSetup {

// B is now first in line to get redeemed, as they both have the same interest rate,
// but B's Trove is younger.

vm.warp(block.timestamp + troveManager.BOOTSTRAP_PERIOD() + 1);

uint256 redemptionAmount = 1000e18; // 1k BOLD

Expand All @@ -106,7 +104,7 @@ contract BasicOps is DevTestSetup {
10,
1e18
);

// Check B's coll and debt reduced
uint256 debt_2 = troveManager.getTroveDebt(B_Id);
assertLt(debt_2, debt_1);
Expand Down
27 changes: 25 additions & 2 deletions contracts/src/test/troveManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ contract TroveManagerTest is DevTestSetup {
uint256 collB1 = troveManager.getTroveColl(BTroveId);
assertGt(collB1, 0);

// fast-forward until redemptions are enabled
vm.warp(block.timestamp + troveManager.BOOTSTRAP_PERIOD() + 1);
// Reduce ETH price so A’s ICR goes below 100%
uint256 newPrice = 1000e18;
priceFeed.setPrice(newPrice);
Expand Down Expand Up @@ -51,4 +49,29 @@ contract TroveManagerTest is DevTestSetup {
uint256 collB2 = troveManager.getTroveColl(BTroveId);
assertLt(collB2, collB1, "B coll mismatch");
}

function testInitialRedemptionBaseRate() public {
assertEq(troveManager.baseRate(), 5e16);
}

function testRedemptionBaseRateAfter2Weeks() public {
assertEq(troveManager.baseRate(), 5e16);

// Two weeks go by
vm.warp(block.timestamp + 14 days);

priceFeed.setPrice(2000e18);
openTroveNoHints100pctMaxFee(A, 200 ether, 200000e18, 1e17);
// A redeems 0.01 BOLD, base rate goes down to almost zero (it’s updated on redemption)
vm.startPrank(A);
troveManager.redeemCollateral(
1e16,
10,
1e18
);
vm.stopPrank();

console.log(troveManager.baseRate(), "baseRate");
assertLt(troveManager.baseRate(), 3e10); // Goes down below 3e-8, i.e., below 0.000003%
}
}
4 changes: 2 additions & 2 deletions contracts/test/BorrowerOperationsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -3551,8 +3551,8 @@ contract("BorrowerOperations", async (accounts) => {

const baseRateBefore = await troveManager.baseRate();

// Artificially make baseRate 5%
await troveManager.setBaseRate(dec(5, 16));
// Artificially make baseRate 6% (higher than the intital 5%)
await troveManager.setBaseRate(dec(6, 16));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, it wasn't immediately obvious why this had to be changed to 6%, but I guess it's because it has to be higher than baseRateBefore, which is now 5%. Should the comment be updated too?

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, right!

await troveManager.setLastFeeOpTimeToNow();

assert.isTrue((await troveManager.baseRate()).gt(baseRateBefore));
Expand Down