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

Update YieldRepurchaseFacility to be independent of RBS #36

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions deployments/.mainnet-1739165363.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"YieldRepurchaseFacility": "0x271e35a8555a62F6bA76508E85dfD76D580B0692"
}
25 changes: 9 additions & 16 deletions src/policies/YieldRepurchaseFacility.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -115,18 +113,16 @@ 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("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();
}
Expand All @@ -149,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 /////////////////////////
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/scripts/deploy/savedDeployments/yieldRepo_v1_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"sequence": [
{
"name": "YieldRepurchaseFacility",
"args": {}
}
]
}
2 changes: 1 addition & 1 deletion src/scripts/env.json
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@
"ReserveMigrator": "0x986b99579BEc7B990331474b66CcDB94Fa2419F5",
"RolesAdmin": "0xb216d714d91eeC4F7120a732c11428857C659eC8",
"TreasuryCustodian": "0xC9518AC915e46D707585116451Dc19c164513Ccf",
"YieldRepurchaseFacility": "0xcaA3d3E653A626e2656d2E799564fE952D39d855",
"YieldRepurchaseFacility": "0x271e35a8555a62F6bA76508E85dfD76D580B0692",
"ZeroDistributor": "0x44a7a09ccddb4338e062f1a3849f9a82bdbf2aaa",
"pOLY": "0xb37796941cA55b7E4243841930C104Ee325Da5a1"
},
Expand Down
105 changes: 35 additions & 70 deletions src/test/policies/YieldRepurchaseFacility.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +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
contract YieldRepurchaseFacilityTest is Test {
Expand All @@ -57,7 +54,6 @@ contract YieldRepurchaseFacilityTest is Test {

Kernel internal kernel;
MockPrice internal PRICE;
OlympusRange internal RANGE;
OlympusTreasury internal TRSRY;
OlympusMinter internal MINTR;
OlympusRoles internal ROLES;
Expand All @@ -67,8 +63,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;
Expand Down Expand Up @@ -112,14 +106,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);
Expand All @@ -140,28 +126,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,
Expand All @@ -180,16 +144,13 @@ 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));
kernel.executeAction(Actions.InstallModule, address(CHREG));

/// 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));
}
{
Expand All @@ -198,9 +159,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
Expand Down Expand Up @@ -231,10 +189,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));

Expand Down Expand Up @@ -365,11 +319,7 @@ 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
Expand All @@ -395,18 +345,25 @@ contract YieldRepurchaseFacilityTest is Test {
// Check that the initial yield was withdrawn from the TRSRY
assertEq(
sReserve.balanceOf(address(TRSRY)),
trsryBalance - sReserve.previewWithdraw(initialYield)
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);
assertEq(
reserve.balanceOf(address(yieldRepo)),
initialYield / 7,
"yieldRepo reserve balance"
);
assertEq(
sReserve.balanceOf(address(yieldRepo)),
sReserve.previewDeposit(initialYield - initialYield / 7)
sReserve.previewDeposit(initialYield - initialYield / 7),
"yieldRepo wrapped reserve balance"
);

// Check that a bond market was not created
assertEq(aggregator.marketCounter(), nextBondMarketId);
// 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");
}

function test_endEpoch_isShutdown() public {
Expand Down Expand Up @@ -527,16 +484,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
Expand All @@ -545,10 +507,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
Expand All @@ -569,19 +536,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");
}
}

Expand Down
Loading