From ea8f492c84acf8c3b32c61ce920f945c2666fe59 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 3 Feb 2025 14:45:29 +0400 Subject: [PATCH 1/8] Committing initial changes on behalf of @ohmzeus --- src/policies/YieldRepurchaseFacility.sol | 21 ++--- .../policies/YieldRepurchaseFacility.t.sol | 80 +------------------ 2 files changed, 8 insertions(+), 93 deletions(-) diff --git a/src/policies/YieldRepurchaseFacility.sol b/src/policies/YieldRepurchaseFacility.sol index ea60dc1b2..841d57182 100644 --- a/src/policies/YieldRepurchaseFacility.sol +++ b/src/policies/YieldRepurchaseFacility.sol @@ -16,7 +16,6 @@ import {IYieldRepo} from "policies/interfaces/IYieldRepo.sol"; import {RolesConsumer, ROLESv1} from "modules/ROLES/OlympusRoles.sol"; import {TRSRYv1} from "modules/TRSRY/TRSRY.v1.sol"; import {PRICEv1} from "modules/PRICE/PRICE.v1.sol"; -import {RANGEv2} from "modules/RANGE/RANGE.v2.sol"; import {CHREGv1} from "modules/CHREG/CHREG.v1.sol"; interface BurnableERC20 { @@ -53,7 +52,6 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { // Modules TRSRYv1 public TRSRY; PRICEv1 public PRICE; - RANGEv2 public RANGE; CHREGv1 public CHREG; // External contracts @@ -118,15 +116,13 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { dependencies = new Keycode[](5); dependencies[0] = toKeycode("TRSRY"); dependencies[1] = toKeycode("PRICE"); - dependencies[2] = toKeycode("RANGE"); - dependencies[3] = toKeycode("CHREG"); - dependencies[4] = toKeycode("ROLES"); + dependencies[2] = toKeycode("CHREG"); + dependencies[3] = toKeycode("ROLES"); TRSRY = TRSRYv1(getModuleAddress(dependencies[0])); PRICE = PRICEv1(getModuleAddress(dependencies[1])); - RANGE = RANGEv2(getModuleAddress(dependencies[2])); - CHREG = CHREGv1(getModuleAddress(dependencies[3])); - ROLES = ROLESv1(getModuleAddress(dependencies[4])); + CHREG = CHREGv1(getModuleAddress(dependencies[2])); + ROLES = ROLESv1(getModuleAddress(dependencies[3])); _oracleDecimals = PRICE.decimals(); } @@ -228,13 +224,10 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { function _createMarket(uint256 bidAmount) internal { // 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, true); // upper wall = (true, true) => high = true, wall = true + // The min price is the inverse of the maximum price of OHM in USDS + uint256 minPrice = 0; // Min price of zero means max price of infinity -- no cap 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 // so the operations assume payoutPriceDecimal is zero and quotePriceDecimals @@ -262,7 +255,7 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { capacityInQuote: false, capacity: bidAmount, formattedInitialPrice: initialPrice.mulDiv(bondScale, oracleScale), - formattedMinimumPrice: minPrice.mulDiv(bondScale, oracleScale), + formattedMinimumPrice: minPrice, debtBuffer: 100_000, // 100% vesting: uint48(0), // Instant swaps conclusion: uint48(block.timestamp + 1 days), // 1 day from now diff --git a/src/test/policies/YieldRepurchaseFacility.t.sol b/src/test/policies/YieldRepurchaseFacility.t.sol index 0d99e9822..f06d67322 100644 --- a/src/test/policies/YieldRepurchaseFacility.t.sol +++ b/src/test/policies/YieldRepurchaseFacility.t.sol @@ -23,14 +23,12 @@ import {IBondAggregator} from "interfaces/IBondAggregator.sol"; import {FullMath} from "libraries/FullMath.sol"; import "src/Kernel.sol"; -import {OlympusRange} from "modules/RANGE/OlympusRange.sol"; import {OlympusTreasury} from "modules/TRSRY/OlympusTreasury.sol"; import {OlympusMinter} from "modules/MINTR/OlympusMinter.sol"; import {OlympusRoles} from "modules/ROLES/OlympusRoles.sol"; import {OlympusClearinghouseRegistry} from "modules/CHREG/OlympusClearinghouseRegistry.sol"; import {RolesAdmin} from "policies/RolesAdmin.sol"; import {YieldRepurchaseFacility} from "policies/YieldRepurchaseFacility.sol"; -import {Operator} from "policies/Operator.sol"; import {BondCallback} from "policies/BondCallback.sol"; // solhint-disable-next-line max-states-count @@ -57,7 +55,6 @@ contract YieldRepurchaseFacilityTest is Test { Kernel internal kernel; MockPrice internal PRICE; - OlympusRange internal RANGE; OlympusTreasury internal TRSRY; OlympusMinter internal MINTR; OlympusRoles internal ROLES; @@ -67,8 +64,6 @@ contract YieldRepurchaseFacilityTest is Test { MockClearinghouse internal clearinghouse; YieldRepurchaseFacility internal yieldRepo; RolesAdmin internal rolesAdmin; - BondCallback internal callback; // only used by operator, not by yieldRepo - Operator internal operator; uint256 initialReserves = 105_000_000e18; uint256 initialConversionRate = 1_05e16; @@ -112,14 +107,6 @@ contract YieldRepurchaseFacilityTest is Test { /// Deploy modules (some mocks) PRICE = new MockPrice(kernel, uint48(8 hours), 10 * 1e18); - RANGE = new OlympusRange( - kernel, - ERC20(ohm), - ERC20(reserve), - uint256(100), - [uint256(1500), uint256(2000)], - [uint256(1500), uint256(2000)] - ); TRSRY = new OlympusTreasury(kernel); MINTR = new OlympusMinter(kernel, address(ohm)); ROLES = new OlympusRoles(kernel); @@ -143,25 +130,6 @@ contract YieldRepurchaseFacilityTest is Test { /// Deploy bond callback callback = new BondCallback(kernel, IBondAggregator(address(aggregator)), ohm); - /// Deploy operator - operator = new Operator( - kernel, - IBondSDA(address(auctioneer)), - callback, - [address(ohm), address(reserve), address(sReserve), address(oldReserve)], - [ - uint32(2000), // cushionFactor - uint32(5 days), // duration - uint32(100_000), // debtBuffer - uint32(1 hours), // depositInterval - uint32(1000), // reserveFactor - uint32(1 hours), // regenWait - uint32(5), // regenThreshold - uint32(7) // regenObserve - // uint32(8 hours) // observationFrequency - ] - ); - /// Deploy protocol loop yieldRepo = new YieldRepurchaseFacility( kernel, @@ -180,7 +148,6 @@ contract YieldRepurchaseFacilityTest is Test { /// Install modules kernel.executeAction(Actions.InstallModule, address(PRICE)); - kernel.executeAction(Actions.InstallModule, address(RANGE)); kernel.executeAction(Actions.InstallModule, address(TRSRY)); kernel.executeAction(Actions.InstallModule, address(MINTR)); kernel.executeAction(Actions.InstallModule, address(ROLES)); @@ -189,7 +156,6 @@ contract YieldRepurchaseFacilityTest is Test { /// Approve policies kernel.executeAction(Actions.ActivatePolicy, address(yieldRepo)); kernel.executeAction(Actions.ActivatePolicy, address(callback)); - kernel.executeAction(Actions.ActivatePolicy, address(operator)); kernel.executeAction(Actions.ActivatePolicy, address(rolesAdmin)); } { @@ -198,9 +164,6 @@ contract YieldRepurchaseFacilityTest is Test { /// YieldRepurchaseFacility ROLES rolesAdmin.grantRole("heart", address(heart)); rolesAdmin.grantRole("loop_daddy", guardian); - - /// Operator ROLES - rolesAdmin.grantRole("operator_admin", address(guardian)); } // Mint tokens to users, clearinghouse, and TRSRY for testing @@ -231,10 +194,6 @@ contract YieldRepurchaseFacilityTest is Test { vm.prank(alice); ohm.approve(address(teller), testOhm * 20); - // Initialise the operator so that the range prices are set - vm.prank(guardian); - operator.initialize(); - // Set principal receivables for the clearinghouse clearinghouse.setPrincipalReceivables(uint256(100_000_000e18)); @@ -365,50 +324,13 @@ contract YieldRepurchaseFacilityTest is Test { marketPrice, ((uint256(1e36) / ((10e18 * 97) / 100)) * 10 ** uint8(36 + 1)) / 10 ** uint8(18 + 1) ); - assertEq( - minPrice, - (((uint256(1e36) / ((10e18 * 120e16) / 1e18))) * 10 ** uint8(36 + 1)) / - 10 ** uint8(18 + 1) - ); + assertEq(minPrice, 0); } // Check that the epoch has been incremented 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 sReserve - _mintYield(); - - // Get the ID of the next bond market from the aggregator - uint256 nextBondMarketId = aggregator.marketCounter(); - - // Cache the TRSRY sDAI balance - uint256 trsryBalance = sReserve.balanceOf(address(TRSRY)); - - vm.prank(heart); - yieldRepo.endEpoch(); - - // Check that the initial yield was withdrawn from the TRSRY - assertEq( - sReserve.balanceOf(address(TRSRY)), - trsryBalance - sReserve.previewWithdraw(initialYield) - ); - - // Check that the yieldRepo contract has the correct reserve balance - assertEq(reserve.balanceOf(address(yieldRepo)), initialYield / 7); - assertEq( - sReserve.balanceOf(address(yieldRepo)), - sReserve.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); From 0ddad7954a3be18206a65c377b2416cdc4868599 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 3 Feb 2025 14:46:10 +0400 Subject: [PATCH 2/8] Correct number for dependencies array --- src/policies/YieldRepurchaseFacility.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/policies/YieldRepurchaseFacility.sol b/src/policies/YieldRepurchaseFacility.sol index 841d57182..2cb1490fd 100644 --- a/src/policies/YieldRepurchaseFacility.sol +++ b/src/policies/YieldRepurchaseFacility.sol @@ -113,7 +113,7 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { } function configureDependencies() external override returns (Keycode[] memory dependencies) { - dependencies = new Keycode[](5); + dependencies = new Keycode[](4); dependencies[0] = toKeycode("TRSRY"); dependencies[1] = toKeycode("PRICE"); dependencies[2] = toKeycode("CHREG"); From 8c06f3c7e566618f547678e0fe9d77d903a36b2b Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 3 Feb 2025 14:46:45 +0400 Subject: [PATCH 3/8] Version bump --- src/policies/YieldRepurchaseFacility.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/policies/YieldRepurchaseFacility.sol b/src/policies/YieldRepurchaseFacility.sol index 2cb1490fd..4078bac36 100644 --- a/src/policies/YieldRepurchaseFacility.sol +++ b/src/policies/YieldRepurchaseFacility.sol @@ -145,7 +145,7 @@ contract YieldRepurchaseFacility is IYieldRepo, Policy, RolesConsumer { /// @return major The major version of the policy. /// @return minor The minor version of the policy. function VERSION() external pure returns (uint8 major, uint8 minor) { - return (1, 1); + return (1, 2); } ///////////////////////// EXTERNAL ///////////////////////// From 339de7c52c64def8cb3d1df6727878f0c3114c0e Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 3 Feb 2025 14:57:38 +0400 Subject: [PATCH 4/8] Fix compiler error --- src/test/policies/YieldRepurchaseFacility.t.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/policies/YieldRepurchaseFacility.t.sol b/src/test/policies/YieldRepurchaseFacility.t.sol index f06d67322..76531c384 100644 --- a/src/test/policies/YieldRepurchaseFacility.t.sol +++ b/src/test/policies/YieldRepurchaseFacility.t.sol @@ -29,7 +29,6 @@ import {OlympusRoles} from "modules/ROLES/OlympusRoles.sol"; import {OlympusClearinghouseRegistry} from "modules/CHREG/OlympusClearinghouseRegistry.sol"; import {RolesAdmin} from "policies/RolesAdmin.sol"; import {YieldRepurchaseFacility} from "policies/YieldRepurchaseFacility.sol"; -import {BondCallback} from "policies/BondCallback.sol"; // solhint-disable-next-line max-states-count contract YieldRepurchaseFacilityTest is Test { @@ -127,9 +126,6 @@ contract YieldRepurchaseFacilityTest is Test { } { - /// Deploy bond callback - callback = new BondCallback(kernel, IBondAggregator(address(aggregator)), ohm); - /// Deploy protocol loop yieldRepo = new YieldRepurchaseFacility( kernel, @@ -155,7 +151,6 @@ contract YieldRepurchaseFacilityTest is Test { /// Approve policies kernel.executeAction(Actions.ActivatePolicy, address(yieldRepo)); - kernel.executeAction(Actions.ActivatePolicy, address(callback)); kernel.executeAction(Actions.ActivatePolicy, address(rolesAdmin)); } { From cee576aeb53c3f6073793087f1ce2386ad4cf23c Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 3 Feb 2025 15:03:33 +0400 Subject: [PATCH 5/8] Fix tests --- .../policies/YieldRepurchaseFacility.t.sol | 75 +++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/src/test/policies/YieldRepurchaseFacility.t.sol b/src/test/policies/YieldRepurchaseFacility.t.sol index 76531c384..44e29745b 100644 --- a/src/test/policies/YieldRepurchaseFacility.t.sol +++ b/src/test/policies/YieldRepurchaseFacility.t.sol @@ -326,6 +326,45 @@ 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 sReserve + _mintYield(); + + // Get the ID of the next bond market from the aggregator + uint256 nextBondMarketId = aggregator.marketCounter(); + + // Cache the TRSRY sDAI balance + uint256 trsryBalance = sReserve.balanceOf(address(TRSRY)); + + vm.prank(heart); + yieldRepo.endEpoch(); + + // Check that the initial yield was withdrawn from the TRSRY + assertEq( + sReserve.balanceOf(address(TRSRY)), + trsryBalance - sReserve.previewWithdraw(initialYield), + "TRSRY wrapped reserve balance" + ); + + // Check that the yieldRepo contract has the correct reserve balance + assertEq( + reserve.balanceOf(address(yieldRepo)), + initialYield / 7, + "yieldRepo reserve balance" + ); + assertEq( + sReserve.balanceOf(address(yieldRepo)), + sReserve.previewDeposit(initialYield - initialYield / 7), + "yieldRepo wrapped reserve balance" + ); + + // Check that a bond market was created + assertEq(aggregator.marketCounter(), nextBondMarketId + 1, "marketCount"); + } + function test_endEpoch_isShutdown() public { // Shutdown the yieldRepo contract vm.prank(guardian); @@ -444,16 +483,21 @@ contract YieldRepurchaseFacilityTest is Test { yieldRepo.endEpoch(); // Check that a new bond market was created - assertEq(aggregator.marketCounter(), nextBondMarketId + 1); + assertEq( + aggregator.marketCounter(), + nextBondMarketId + 1, + "bond market id should be incremented" + ); // Check that the yieldRepo contract burned the OHM - assertEq(ohm.balanceOf(address(yieldRepo)), 0); + assertEq(ohm.balanceOf(address(yieldRepo)), 0, "OHM should be burned"); // Check that the treasury balance has changed by the amount of backing withdrawn for the burnt OHM uint256 reserveFromBurnedOhm = 100e9 * yieldRepo.backingPerToken(); assertEq( sReserve.balanceOf(address(TRSRY)), - trsryBalance - sReserve.previewWithdraw(reserveFromBurnedOhm) + trsryBalance - sReserve.previewWithdraw(reserveFromBurnedOhm), + "treasury balance should decrease by the amount of backing withdrawn for the burnt OHM" ); // Check that the balance of the yieldRepo contract has changed correctly @@ -462,10 +506,15 @@ contract YieldRepurchaseFacilityTest is Test { reserveFromBurnedOhm) / 6; // Check that the yieldRepo contract reserve balances have changed correctly - assertEq(reserve.balanceOf(address(yieldRepo)), expectedBidAmount); + assertEq( + reserve.balanceOf(address(yieldRepo)), + expectedBidAmount, + "reserve balance should increase by the bid amount" + ); assertGe( sReserve.balanceOf(address(yieldRepo)), - yieldRepoWrappedReserveBalance - sReserve.previewWithdraw(expectedBidAmount) + yieldRepoWrappedReserveBalance - sReserve.previewWithdraw(expectedBidAmount), + "wrapped reserve balance should decrease by the bid amount" ); // Confirm that the bond market has the correct configuration @@ -486,19 +535,17 @@ contract YieldRepurchaseFacilityTest is Test { uint256 scale ) = auctioneer.markets(nextBondMarketId); - assertEq(capacity, expectedBidAmount); - assertEq(maxPayout, capacity / 6); + assertEq(capacity, expectedBidAmount, "capacity should be the bid amount"); + assertEq(maxPayout, capacity / 6, "max payout should be 1/6th of the capacity"); - assertEq(scale, 10 ** uint8(36 + 18 - 9 + 0)); + assertEq(scale, 10 ** uint8(36 + 18 - 9 + 0), "scale"); assertEq( marketPrice, - ((uint256(1e36) / ((10e18 * 97) / 100)) * 10 ** uint8(36 + 1)) / 10 ** uint8(18 + 1) - ); - assertEq( - minPrice, - (((uint256(1e36) / ((10e18 * 120e16) / 1e18))) * 10 ** uint8(36 + 1)) / - 10 ** uint8(18 + 1) + ((uint256(1e36) / ((10e18 * 97) / 100)) * 10 ** uint8(36 + 1)) / + 10 ** uint8(18 + 1), + "marketPrice" ); + assertEq(minPrice, 0, "minPrice"); } } From 4646f00cf33753b8483d91bec3fe180a1055aff7 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 3 Feb 2025 15:08:06 +0400 Subject: [PATCH 6/8] Documentation --- src/test/policies/YieldRepurchaseFacility.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/policies/YieldRepurchaseFacility.t.sol b/src/test/policies/YieldRepurchaseFacility.t.sol index 44e29745b..0ab57daac 100644 --- a/src/test/policies/YieldRepurchaseFacility.t.sol +++ b/src/test/policies/YieldRepurchaseFacility.t.sol @@ -362,6 +362,7 @@ contract YieldRepurchaseFacilityTest is Test { ); // Check that a bond market was created + // This is because the current price is greater than the wall, and the wall no longer prevents a new bond market from being created assertEq(aggregator.marketCounter(), nextBondMarketId + 1, "marketCount"); } From d8cf6fe7c0ee791f5a808a6e5db8d8bbee8dbe12 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 10 Feb 2025 10:26:05 +0500 Subject: [PATCH 7/8] Deployment file for YRF --- src/scripts/deploy/savedDeployments/yieldRepo_v1_2.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/scripts/deploy/savedDeployments/yieldRepo_v1_2.json diff --git a/src/scripts/deploy/savedDeployments/yieldRepo_v1_2.json b/src/scripts/deploy/savedDeployments/yieldRepo_v1_2.json new file mode 100644 index 000000000..9bba6e624 --- /dev/null +++ b/src/scripts/deploy/savedDeployments/yieldRepo_v1_2.json @@ -0,0 +1,8 @@ +{ + "sequence": [ + { + "name": "YieldRepurchaseFacility", + "args": {} + } + ] +} \ No newline at end of file From e5e115fbf7966ff6eee25e33e4a9a0e3983e350d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 10 Feb 2025 10:31:46 +0500 Subject: [PATCH 8/8] Deployment address for YRF 1.2 --- deployments/.mainnet-1739165363.json | 3 +++ src/scripts/env.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 deployments/.mainnet-1739165363.json diff --git a/deployments/.mainnet-1739165363.json b/deployments/.mainnet-1739165363.json new file mode 100644 index 000000000..e736e73f2 --- /dev/null +++ b/deployments/.mainnet-1739165363.json @@ -0,0 +1,3 @@ +{ + "YieldRepurchaseFacility": "0x271e35a8555a62F6bA76508E85dfD76D580B0692" +} diff --git a/src/scripts/env.json b/src/scripts/env.json index e096ad4ee..d92a88853 100644 --- a/src/scripts/env.json +++ b/src/scripts/env.json @@ -396,7 +396,7 @@ "ReserveMigrator": "0x986b99579BEc7B990331474b66CcDB94Fa2419F5", "RolesAdmin": "0xb216d714d91eeC4F7120a732c11428857C659eC8", "TreasuryCustodian": "0xC9518AC915e46D707585116451Dc19c164513Ccf", - "YieldRepurchaseFacility": "0xcaA3d3E653A626e2656d2E799564fE952D39d855", + "YieldRepurchaseFacility": "0x271e35a8555a62F6bA76508E85dfD76D580B0692", "ZeroDistributor": "0x44a7a09ccddb4338e062f1a3849f9a82bdbf2aaa", "pOLY": "0xb37796941cA55b7E4243841930C104Ee325Da5a1" },