Skip to content
Draft
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: 4 additions & 0 deletions ROLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This document describes the roles that are used in the Olympus protocol.
| admin | Heart | Reset the heartbeat, enable/disable the contract,set the distributor, set auction rewards |
| admin | MonoCooler | Allows setting parameters on the MonoCooler |
| admin | ReserveWrapper | Enable/disable contract |
| admin | YieldDepositFacility | Set yield fee, enable/disable contract |
| bondmanager_admin | BondManager | Create/close bond markets, set parameters |
| bridge_admin | CrossChainBridge | Allows configuring the CrossChainBridge |
| callback_admin | BondCallback | Administers the policy |
Expand All @@ -38,19 +39,22 @@ This document describes the roles that are used in the Olympus protocol.
| emergency | Heart | Disable the contract |
| emergency | MonoCooler | Allows enable/disable on the MonoCooler |
| emergency | ReserveWrapper | Disable contract |
| emergency | YieldDepositFacility | Disable the contract |
| emergency_restart | Emergency | Reactivates the TRSRY and/or MINTR modules |
| emergency_shutdown | Clearinghouse | Allows shutting down the protocol in an emergency |
| emergency_shutdown | Emergency | Deactivates the TRSRY and/or MINTR modules |
| heart | ConvertibleDepositFacility | Calls the execute() function |
| heart | EmissionManager | Calls the execute() function |
| heart | Operator | Call the operate() function |
| heart | ReserveMigrator | Allows migrating reserves from one reserve token to another |
| heart | YieldDepositFacility | Calls the execute() function |
| heart | YieldRepurchaseFacility | Creates a new YRF market |
| loan_consolidator_admin | LoanConsolidator | Allows configuring the LoanConsolidator |
| manager | ConvertibleDepositAuctioneer | Set tracking period, set tick step, enable/disable deposit periods |
| manager | DepositManager | Add asset definition, set asset deposit cap, add/enable/disable asset periods, set deposit reclaim rate |
| manager | DepositRedemptionVault | Set max borrow percentage, set interest rate, set claim default reward percentage |
| manager | Heart | Reset the heartbeat |
| manager | YieldDepositFacility | Set yield fee |
| operator_admin | Operator | Activate/deactivate the functionality |
| operator_policy | Operator | Set spreads, threshold factor, and cushion factor |
| operator_reporter | Operator | Report bond purchases |
Expand Down
518 changes: 518 additions & 0 deletions src/policies/deposits/YieldDepositFacility.sol

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions src/policies/interfaces/deposits/IYieldDepositFacility.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "src/interfaces/IERC20.sol";

/// @title IYieldDepositFacility
/// @notice Interface for the Yield Facility that can be used to mint yield-bearing deposits
interface IYieldDepositFacility {
// ========== EVENTS ========== //

event CreatedDeposit(
address indexed asset,
address indexed depositor,
uint256 indexed positionId,
uint8 periodMonths,
uint256 depositAmount
);

event ClaimedYield(address indexed asset, address indexed depositor, uint256 yield);

event YieldFeeSet(uint16 yieldFee);

event RateSnapshotTaken(address indexed vault, uint48 timestamp, uint256 rate);

// ========== ERRORS ========== //

error YDF_InvalidArgs(string reason_);

error YDF_NotOwner(uint256 positionId_);

error YDF_InvalidToken(address token_, uint8 periodMonths_);

error YDF_Unsupported(uint256 positionId_);

error YDF_NoRateSnapshot(address vault_, uint48 timestamp_);

// ========== DATA STRUCTURES ========== //

/// @notice Parameters for the {createPosition} function
///
/// @param asset The address of the asset
/// @param periodMonths The period of the deposit
/// @param amount The amount of asset to deposit
/// @param wrapPosition Whether the position should be wrapped
/// @param wrapReceipt Whether the receipt token should be wrapped
struct CreatePositionParams {
IERC20 asset;
uint8 periodMonths;
uint256 amount;
bool wrapPosition;
bool wrapReceipt;
}

// ========== MINT ========== //

/// @notice Creates a position for a yield-bearing deposit
/// @dev The implementing contract is expected to handle the following:
/// - Validating that the asset is supported
/// - Depositing the asset into the deposit manager and minting the receipt token
/// - Creating a new position in the DEPOS module
///
/// @param params_ The parameters for the position creation
/// @return positionId The ID of the new position
/// @return receiptTokenId The ID of the receipt token
/// @return actualAmount The quantity of receipt tokens minted to the depositor
function createPosition(
CreatePositionParams calldata params_
) external returns (uint256 positionId, uint256 receiptTokenId, uint256 actualAmount);

/// @notice Deposits the given amount of the underlying asset in exchange for a receipt token. This function can be used to mint additional receipt tokens on a 1:1 basis, without creating a new position.
///
/// @param asset_ The address of the asset
/// @param periodMonths_ The period of the deposit
/// @param amount_ The amount of asset to deposit
/// @param wrapReceipt_ Whether the receipt token should be wrapped
/// @return receiptTokenId The ID of the receipt token
/// @return actualAmount The quantity of receipt tokens minted to the depositor
function deposit(
IERC20 asset_,
uint8 periodMonths_,
uint256 amount_,
bool wrapReceipt_
) external returns (uint256 receiptTokenId, uint256 actualAmount);

// ========== YIELD ========== //

/// @notice Preview the amount of yield that would be claimed for the given positions
/// @dev The implementing contract is expected to handle the following:
/// - Validating that `account_` is the owner of all of the positions
/// - Validating that token in the position is a supported receipt token
/// - Validating that all of the positions are valid
/// - Returning the total amount of yield that would be claimed
///
/// @param account_ The address to preview the yield for
/// @param positionIds_ An array of position ids that will be claimed
/// @return yield The amount of yield that would be claimed
/// @return asset The address of the asset that will be received
function previewClaimYield(
address account_,
uint256[] memory positionIds_
) external view returns (uint256 yield, IERC20 asset);

/// @notice Claims the yield for the given positions
/// @dev The implementing contract is expected to handle the following:
/// - Validating that the caller is the owner of all of the positions
/// - Validating that token in the position is a supported receipt token
/// - Validating that all of the positions are valid
/// - Transferring the yield to the caller
/// - Emitting an event
///
/// @param positionIds_ An array of position ids that will be claimed
/// @return yield The amount of yield that was claimed
function claimYield(uint256[] memory positionIds_) external returns (uint256 yield);

// ========== ADMIN FUNCTIONS ========== //

/// @notice Sets the percentage of yield that will be taken as a fee
/// @dev The implementing contract is expected to handle the following:
/// - Validating that the caller has the correct role
/// - Setting the yield fee
///
/// @param yieldFee_ The percentage of yield that will be taken as a fee, in terms of 100e2
function setYieldFee(uint16 yieldFee_) external;

/// @notice Returns the percentage of yield that will be taken as a fee
///
/// @return yieldFee The percentage of yield that will be taken as a fee, in terms of 100e2
function getYieldFee() external view returns (uint16 yieldFee);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {IERC4626} from "src/interfaces/IERC4626.sol";
import {Kernel, Actions} from "src/Kernel.sol";
import {DepositRedemptionVault} from "src/policies/deposits/DepositRedemptionVault.sol";
import {ConvertibleDepositFacility} from "src/policies/deposits/ConvertibleDepositFacility.sol";
import {YieldDepositFacility} from "src/policies/deposits/YieldDepositFacility.sol";
import {OlympusTreasury} from "src/modules/TRSRY/OlympusTreasury.sol";
import {OlympusMinter} from "src/modules/MINTR/OlympusMinter.sol";
import {OlympusRoles} from "src/modules/ROLES/OlympusRoles.sol";
Expand All @@ -23,6 +24,7 @@ import {IEnabler} from "src/periphery/interfaces/IEnabler.sol";
import {IDepositManager} from "src/policies/interfaces/deposits/IDepositManager.sol";
import {IDepositRedemptionVault} from "src/policies/interfaces/deposits/IDepositRedemptionVault.sol";
import {IConvertibleDepositFacility} from "src/policies/interfaces/deposits/IConvertibleDepositFacility.sol";
import {IYieldDepositFacility} from "src/policies/interfaces/deposits/IYieldDepositFacility.sol";
import {IDepositFacility} from "src/policies/interfaces/deposits/IDepositFacility.sol";
import {ERC6909} from "@openzeppelin-5.3.0/token/ERC6909/draft-ERC6909.sol";
import {IPolicyAdmin} from "src/policies/interfaces/utils/IPolicyAdmin.sol";
Expand All @@ -32,7 +34,7 @@ contract DepositRedemptionVaultTest is Test {
Kernel public kernel;
DepositRedemptionVault public redemptionVault;
ConvertibleDepositFacility public cdFacility;
ConvertibleDepositFacility public cdFacilityTwo;
YieldDepositFacility public ydFacility;
OlympusTreasury public treasury;
OlympusMinter public minter;
OlympusRoles public roles;
Expand All @@ -42,7 +44,7 @@ contract DepositRedemptionVaultTest is Test {
ReceiptTokenManager public receiptTokenManager;

address public cdFacilityAddress;
address public cdFacilityTwoAddress;
address public ydFacilityAddress;

MockERC20 public ohm;
MockERC20 public reserveToken;
Expand Down Expand Up @@ -126,8 +128,8 @@ contract DepositRedemptionVaultTest is Test {
redemptionVault = new DepositRedemptionVault(address(kernel), address(depositManager));
cdFacility = new ConvertibleDepositFacility(address(kernel), address(depositManager));
cdFacilityAddress = address(cdFacility);
cdFacilityTwo = new ConvertibleDepositFacility(address(kernel), address(depositManager));
cdFacilityTwoAddress = address(cdFacilityTwo);
ydFacility = new YieldDepositFacility(address(kernel), address(depositManager));
ydFacilityAddress = address(ydFacility);
rolesAdmin = new RolesAdmin(kernel);

// Install modules
Expand All @@ -138,15 +140,15 @@ contract DepositRedemptionVaultTest is Test {
kernel.executeAction(Actions.ActivatePolicy, address(depositManager));
kernel.executeAction(Actions.ActivatePolicy, address(redemptionVault));
kernel.executeAction(Actions.ActivatePolicy, address(cdFacility));
kernel.executeAction(Actions.ActivatePolicy, address(cdFacilityTwo));
kernel.executeAction(Actions.ActivatePolicy, address(ydFacility));
kernel.executeAction(Actions.ActivatePolicy, address(rolesAdmin));

// Grant roles
rolesAdmin.grantRole(bytes32("cd_auctioneer"), auctioneer);
rolesAdmin.grantRole(bytes32("emergency"), emergency);
rolesAdmin.grantRole(bytes32("admin"), admin);
rolesAdmin.grantRole(bytes32("deposit_operator"), address(cdFacility));
rolesAdmin.grantRole(bytes32("deposit_operator"), address(cdFacilityTwo));
rolesAdmin.grantRole(bytes32("deposit_operator"), address(ydFacility));
rolesAdmin.grantRole(bytes32("heart"), HEART);
rolesAdmin.grantRole(bytes32("manager"), manager);

Expand All @@ -163,21 +165,21 @@ contract DepositRedemptionVaultTest is Test {
cdFacility.enable("");

vm.prank(admin);
cdFacilityTwo.enable("");
ydFacility.enable("");

// Register facilities in redemption vault
vm.prank(admin);
redemptionVault.authorizeFacility(address(cdFacility));

vm.prank(admin);
redemptionVault.authorizeFacility(address(cdFacilityTwo));
redemptionVault.authorizeFacility(address(ydFacility));

// Create receipt tokens
vm.startPrank(admin);

// Set the facility names
depositManager.setOperatorName(address(cdFacility), "cdf");
depositManager.setOperatorName(address(cdFacilityTwo), "cdg");
depositManager.setOperatorName(address(ydFacility), "ydf");

depositManager.addAsset(
IERC20(address(reserveToken)),
Expand All @@ -193,11 +195,11 @@ contract DepositRedemptionVaultTest is Test {
address(cdFacility)
);

// Enable the token/period/facility combo for the second facility
// Enable the token/period/facility combo for the YieldDepositFacility
depositManager.addAssetPeriod(
IERC20(address(reserveToken)),
PERIOD_MONTHS,
address(cdFacilityTwo)
address(ydFacility)
);

receiptTokenId = depositManager.getReceiptTokenId(
Expand All @@ -208,7 +210,7 @@ contract DepositRedemptionVaultTest is Test {

// Set the reclaim rate
cdFacility.setAssetPeriodReclaimRate(IERC20(address(reserveToken)), PERIOD_MONTHS, 90e2);
cdFacilityTwo.setAssetPeriodReclaimRate(IERC20(address(reserveToken)), PERIOD_MONTHS, 90e2);
ydFacility.setAssetPeriodReclaimRate(IERC20(address(reserveToken)), PERIOD_MONTHS, 90e2);
vm.stopPrank();

// Create a second receipt token
Expand All @@ -227,11 +229,11 @@ contract DepositRedemptionVaultTest is Test {
address(cdFacility)
);

// Enable the token/period/facility combo for the second facility
// Enable the token/period/facility combo for the YieldDepositFacility
depositManager.addAssetPeriod(
IERC20(address(reserveTokenTwo)),
PERIOD_MONTHS,
address(cdFacilityTwo)
address(ydFacility)
);

receiptTokenIdTwo = depositManager.getReceiptTokenId(
Expand All @@ -242,11 +244,7 @@ contract DepositRedemptionVaultTest is Test {

// Set the reclaim rate
cdFacility.setAssetPeriodReclaimRate(IERC20(address(reserveTokenTwo)), PERIOD_MONTHS, 90e2);
cdFacilityTwo.setAssetPeriodReclaimRate(
IERC20(address(reserveTokenTwo)),
PERIOD_MONTHS,
90e2
);
ydFacility.setAssetPeriodReclaimRate(IERC20(address(reserveTokenTwo)), PERIOD_MONTHS, 90e2);
vm.stopPrank();

// Authorize the redemption vault to interact with CD Facility
Expand Down Expand Up @@ -432,14 +430,12 @@ contract DepositRedemptionVaultTest is Test {
) internal returns (uint256 positionId) {
uint256 actualAmount;

vm.prank(auctioneer);
(positionId, , actualAmount) = cdFacilityTwo.createPosition(
IConvertibleDepositFacility.CreatePositionParams({
vm.prank(account_);
(positionId, , actualAmount) = ydFacility.createPosition(
IYieldDepositFacility.CreatePositionParams({
asset: iReserveToken,
periodMonths: PERIOD_MONTHS,
depositor: account_,
amount: amount_,
conversionPrice: CONVERSION_PRICE,
wrapPosition: false,
wrapReceipt: false
})
Expand Down Expand Up @@ -641,11 +637,11 @@ contract DepositRedemptionVaultTest is Test {
_;
}

// modifier givenYieldFee(uint16 yieldFee_) {
// vm.prank(admin);
// ydFacility.setYieldFee(yieldFee_);
// _;
// }
modifier givenYieldFee(uint16 yieldFee_) {
vm.prank(admin);
ydFacility.setYieldFee(yieldFee_);
_;
}

modifier givenDepositPeriodEnded(uint48 elapsed_) {
vm.warp(YIELD_EXPIRY + elapsed_);
Expand All @@ -654,7 +650,7 @@ contract DepositRedemptionVaultTest is Test {

function _takeRateSnapshot() internal {
vm.prank(HEART);
cdFacilityTwo.execute();
ydFacility.execute();
}

modifier givenRateSnapshotTaken() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,23 @@ contract DepositRedemptionVaultAuthorizeFacilityTest is DepositRedemptionVaultTe
function test_multipleFacilities()
public
givenFacilityIsDeauthorized(cdFacilityAddress)
givenFacilityIsDeauthorized(address(cdFacilityTwo))
givenFacilityIsDeauthorized(address(ydFacility))
{
// Authorize first facility
vm.prank(admin);
redemptionVault.authorizeFacility(cdFacilityAddress);

// Authorize second facility
vm.prank(admin);
redemptionVault.authorizeFacility(address(cdFacilityTwo));
redemptionVault.authorizeFacility(address(ydFacility));

// Assert both facilities are registered
assertTrue(
redemptionVault.isAuthorizedFacility(cdFacilityAddress),
"First facility should be registered"
);
assertTrue(
redemptionVault.isAuthorizedFacility(address(cdFacilityTwo)),
redemptionVault.isAuthorizedFacility(address(ydFacility)),
"Second facility should be registered"
);

Expand All @@ -135,7 +135,7 @@ contract DepositRedemptionVaultAuthorizeFacilityTest is DepositRedemptionVaultTe
if (facilities[i] == cdFacilityAddress) {
foundFirst = true;
}
if (facilities[i] == address(cdFacilityTwo)) {
if (facilities[i] == address(ydFacility)) {
foundSecond = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ contract DepositRedemptionVaultDeauthorizeFacilityTest is DepositRedemptionVault
vm.prank(admin);
redemptionVault.deauthorizeFacility(cdFacilityAddress);
vm.prank(admin);
redemptionVault.deauthorizeFacility(cdFacilityTwoAddress);
redemptionVault.deauthorizeFacility(ydFacilityAddress);

// Assert all facilities are deauthorized
assertFalse(
Expand Down
Loading
Loading