diff --git a/src/policies/Heart.sol b/src/policies/Heart.sol index c576cc01..86ff0213 100644 --- a/src/policies/Heart.sol +++ b/src/policies/Heart.sol @@ -10,6 +10,7 @@ import {IDistributor} from "policies/interfaces/IDistributor.sol"; import {IOperator} from "policies/interfaces/IOperator.sol"; import {IYieldRepo} from "policies/interfaces/IYieldRepo.sol"; import {IHeart} from "policies/interfaces/IHeart.sol"; +import {IStaking} from "interfaces/IStaking.sol"; import {RolesConsumer, ROLESv1} from "modules/ROLES/OlympusRoles.sol"; import {PRICEv1} from "modules/PRICE/PRICE.v1.sol"; diff --git a/src/policies/YieldRepurchaseFacility.sol b/src/policies/YieldRepurchaseFacility.sol index 19659d4f..43176b84 100644 --- a/src/policies/YieldRepurchaseFacility.sol +++ b/src/policies/YieldRepurchaseFacility.sol @@ -218,8 +218,11 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { // Calculate inverse prices from the oracle feed // The start price is the current market price, which is also the last price since this is called on a heartbeat // The min price is the upper cushion price, since we don't want to buy above this level - uint256 minPrice = 10 ** (_oracleDecimals * 2) / RANGE.price(true, false); // upper cushion = (true, false) => high = true, wall = false - uint256 initialPrice = 10 ** (_oracleDecimals * 2) / PRICE.getLastPrice(); + uint256 minPrice = 10 ** (_oracleDecimals * 2) / RANGE.price(true, true); // upper wall = (true, true) => high = true, wall = true + uint256 initialPrice = 10 ** (_oracleDecimals * 2) / ((PRICE.getLastPrice() * 97) / 100); // 3% below current stated price in case oracle is stale + + // If the min price is greater than or equal to the initial price, we don't want to create a market + if (minPrice >= initialPrice) return; // Calculate scaleAdjustment for bond market // Price decimals are returned from the perspective of the quote token @@ -329,4 +332,4 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { balance = ohm.balanceOf(address(this)); backing = balance * backingPerToken; } -} +} \ No newline at end of file diff --git a/src/test/policies/Heart.t.sol b/src/test/policies/Heart.t.sol index f3ea5708..fc120fb4 100644 --- a/src/test/policies/Heart.t.sol +++ b/src/test/policies/Heart.t.sol @@ -497,4 +497,4 @@ contract HeartTest is Test { vm.expectRevert(err); heart.setRewardAuctionParams(uint256(2e18), uint48(12 * 25)); } -} +} \ No newline at end of file diff --git a/src/test/policies/YieldRepurchaseFacility.t.sol b/src/test/policies/YieldRepurchaseFacility.t.sol index 218da8af..436e2ae5 100644 --- a/src/test/policies/YieldRepurchaseFacility.t.sol +++ b/src/test/policies/YieldRepurchaseFacility.t.sol @@ -109,8 +109,8 @@ contract YieldRepurchaseFacilityTest is Test { ERC20(ohm), ERC20(reserve), uint256(100), - [uint256(2000), uint256(2500)], - [uint256(2000), uint256(2500)] + [uint256(1500), uint256(2000)], + [uint256(1500), uint256(2000)] ); TRSRY = new OlympusTreasury(kernel); MINTR = new OlympusMinter(kernel, address(ohm)); @@ -259,7 +259,9 @@ contract YieldRepurchaseFacilityTest is Test { // [X] when epoch == epochLength // [X] The yield earned on the wrapped reserves over the past 21 epochs is withdrawn from the TRSRY (affecting the balanceInDai and bidAmount) // [X] OHM in the contract is burned and reserves are added at the backing rate - // [X] a new bond market is created with correct bid amount + // [X] given current price is less than upper wall + // [ ] a new bond market is created with correct bid amount + // [X] given current price is greater than or equal to upper wall // [X] when epoch != epochLength // [X] OHM in the contract is burned and reserves are added at the backing rate // [X] a new bond market is created with correct bid amount @@ -291,7 +293,7 @@ contract YieldRepurchaseFacilityTest is Test { assertEq(yieldRepo.epoch(), 20); } - function test_endEpoch_firstCall() public { + function test_endEpoch_firstCall_currentLessThanWall() public { // Mint yield to the wrappedReserve _mintYield(); @@ -349,7 +351,7 @@ contract YieldRepurchaseFacilityTest is Test { assertEq(scale, 10 ** uint8(36 + 18 - 9 + 0)); assertEq( marketPrice, - ((uint256(1e36) / 10e18) * 10 ** uint8(36 + 1)) / 10 ** uint8(18 + 1) + ((uint256(1e36) / ((10e18 * 97) / 100)) * 10 ** uint8(36 + 1)) / 10 ** uint8(18 + 1) ); assertEq( minPrice, @@ -362,6 +364,39 @@ contract YieldRepurchaseFacilityTest is Test { assertEq(yieldRepo.epoch(), 0); } + function test_endEpoch_firstCall_currentGreaterThanWall() public { + // Change the current price to be greater than the wall + PRICE.setLastPrice(15 * 1e18); + + // Mint yield to the wrappedReserve + _mintYield(); + + // Get the ID of the next bond market from the aggregator + uint256 nextBondMarketId = aggregator.marketCounter(); + + // Cache the TRSRY sDAI balance + uint256 trsryBalance = wrappedReserve.balanceOf(address(TRSRY)); + + vm.prank(heart); + yieldRepo.endEpoch(); + + // Check that the initial yield was withdrawn from the TRSRY + assertEq( + wrappedReserve.balanceOf(address(TRSRY)), + trsryBalance - wrappedReserve.previewWithdraw(initialYield) + ); + + // Check that the yieldRepo contract has the correct reserve balance + assertEq(reserve.balanceOf(address(yieldRepo)), initialYield / 7); + assertEq( + wrappedReserve.balanceOf(address(yieldRepo)), + wrappedReserve.previewDeposit(initialYield - initialYield / 7) + ); + + // Check that a bond market was not created + assertEq(aggregator.marketCounter(), nextBondMarketId); + } + function test_endEpoch_isShutdown() public { // Shutdown the yieldRepo contract vm.prank(guardian); @@ -528,7 +563,7 @@ contract YieldRepurchaseFacilityTest is Test { assertEq(scale, 10 ** uint8(36 + 18 - 9 + 0)); assertEq( marketPrice, - ((uint256(1e36) / 10e18) * 10 ** uint8(36 + 1)) / 10 ** uint8(18 + 1) + ((uint256(1e36) / ((10e18 * 97) / 100)) * 10 ** uint8(36 + 1)) / 10 ** uint8(18 + 1) ); assertEq( minPrice, @@ -700,4 +735,4 @@ contract YieldRepurchaseFacilityTest is Test { // Confirm the view function matches assertEq(yieldRepo.getNextYield(), expectedNextYield); } -} +} \ No newline at end of file