ConvertibleDepositFacility

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/policies/deposits/ConvertibleDepositFacility.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
/// forge-lint: disable-start(mixed-case-function, mixed-case-variable)
pragma solidity >=0.8.20;

// Interfaces
import {IERC20} from "src/interfaces/IERC20.sol";
import {IERC165} from "@openzeppelin-5.3.0/interfaces/IERC165.sol";
import {IConvertibleDepositFacility} from "src/policies/interfaces/deposits/IConvertibleDepositFacility.sol";
import {IDepositManager} from "src/policies/interfaces/deposits/IDepositManager.sol";
import {IDepositPositionManager} from "src/modules/DEPOS/IDepositPositionManager.sol";
import {IPeriodicTask} from "src/interfaces/IPeriodicTask.sol";

// Libraries
import {FullMath} from "src/libraries/FullMath.sol";

// Bophades
import {Keycode, Permissions, Policy, toKeycode} from "src/Kernel.sol";
import {ROLESv1} from "src/modules/ROLES/ROLES.v1.sol";
import {MINTRv1} from "src/modules/MINTR/MINTR.v1.sol";
import {TRSRYv1} from "src/modules/TRSRY/TRSRY.v1.sol";
import {DEPOSv1} from "src/modules/DEPOS/DEPOS.v1.sol";
import {HEART_ROLE} from "src/policies/utils/RoleDefinitions.sol";
import {BaseDepositFacility} from "src/policies/deposits/BaseDepositFacility.sol";

/// @title  Convertible Deposit Facility
/// @notice Implementation of the {IConvertibleDepositFacility} interface
///         It is a general-purpose contract that can be used to create, mint, convert, redeem, and reclaim receipt tokens
contract ConvertibleDepositFacility is
    BaseDepositFacility,
    IConvertibleDepositFacility,
    IPeriodicTask
{
    // ========== CONSTANTS ========== //

    bytes32 public constant ROLE_AUCTIONEER = "cd_auctioneer";

    // ========== STATE VARIABLES ========== //

    /// @notice The MINTR module.
    MINTRv1 public MINTR;

    uint256 internal constant _OHM_SCALE = 1e9;

    // ========== SETUP ========== //

    constructor(
        address kernel_,
        address depositManager_
    ) BaseDepositFacility(kernel_, depositManager_) {
        // Disabled by default by PolicyEnabler
    }

    /// @inheritdoc Policy
    function configureDependencies() external override returns (Keycode[] memory dependencies) {
        dependencies = new Keycode[](4);
        dependencies[0] = toKeycode("TRSRY");
        dependencies[1] = toKeycode("MINTR");
        dependencies[2] = toKeycode("ROLES");
        dependencies[3] = toKeycode("DEPOS");

        TRSRY = TRSRYv1(getModuleAddress(dependencies[0]));
        MINTR = MINTRv1(getModuleAddress(dependencies[1]));
        ROLES = ROLESv1(getModuleAddress(dependencies[2]));
        DEPOS = DEPOSv1(getModuleAddress(dependencies[3]));

        // Validate that the OHM scale is the same
        uint256 ohmScale = 10 ** uint256(MINTR.ohm().decimals());
        if (ohmScale != _OHM_SCALE) revert CDF_InvalidArgs("OHM decimals");
    }

    /// @inheritdoc Policy
    function requestPermissions()
        external
        view
        override
        returns (Permissions[] memory permissions)
    {
        Keycode mintrKeycode = toKeycode("MINTR");
        Keycode deposKeycode = toKeycode("DEPOS");

        permissions = new Permissions[](5);
        permissions[0] = Permissions({
            keycode: mintrKeycode,
            funcSelector: MINTR.increaseMintApproval.selector
        });
        permissions[1] = Permissions({keycode: mintrKeycode, funcSelector: MINTR.mintOhm.selector});
        permissions[2] = Permissions({keycode: deposKeycode, funcSelector: DEPOS.mint.selector});
        permissions[3] = Permissions({
            keycode: deposKeycode,
            funcSelector: DEPOS.setRemainingDeposit.selector
        });
        permissions[4] = Permissions({keycode: deposKeycode, funcSelector: DEPOS.split.selector});
    }

    function VERSION() external pure returns (uint8 major, uint8 minor) {
        major = 1;
        minor = 0;

        return (major, minor);
    }

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

    /// @inheritdoc IConvertibleDepositFacility
    /// @dev        This function reverts if:
    ///             - The caller does not have the ROLE_AUCTIONEER role
    ///             - The contract is not enabled
    ///             - The asset and period are not supported
    function createPosition(
        CreatePositionParams calldata params_
    )
        external
        onlyRole(ROLE_AUCTIONEER)
        nonReentrant
        onlyEnabled
        returns (uint256 positionId, uint256 receiptTokenId, uint256 actualAmount)
    {
        // Deposit the asset into the deposit manager
        // This will validate that the asset is supported, and mint the receipt token
        (receiptTokenId, actualAmount) = DEPOSIT_MANAGER.deposit(
            IDepositManager.DepositParams({
                asset: params_.asset,
                depositPeriod: params_.periodMonths,
                depositor: params_.depositor,
                amount: params_.amount,
                shouldWrap: params_.wrapReceipt
            })
        );

        // Create a new position in the DEPOS module
        positionId = DEPOS.mint(
            IDepositPositionManager.MintParams({
                owner: params_.depositor,
                asset: address(params_.asset),
                periodMonths: params_.periodMonths,
                remainingDeposit: actualAmount,
                conversionPrice: params_.conversionPrice,
                expiry: uint48(block.timestamp + uint48(params_.periodMonths) * 30 days),
                wrapPosition: params_.wrapPosition,
                additionalData: ""
            })
        );

        // Emit an event
        emit CreatedDeposit(
            address(params_.asset),
            params_.depositor,
            positionId,
            params_.periodMonths,
            actualAmount
        );

        return (positionId, receiptTokenId, actualAmount);
    }

    /// @inheritdoc IConvertibleDepositFacility
    function deposit(
        IERC20 asset_,
        uint8 periodMonths_,
        uint256 amount_,
        bool wrapReceipt_
    ) external nonReentrant onlyEnabled returns (uint256 receiptTokenId, uint256 actualAmount) {
        // Deposit the asset into the deposit manager and get the receipt token back
        // This will revert if the asset is not supported
        (receiptTokenId, actualAmount) = DEPOSIT_MANAGER.deposit(
            IDepositManager.DepositParams({
                asset: asset_,
                depositPeriod: periodMonths_,
                depositor: msg.sender,
                amount: amount_,
                shouldWrap: wrapReceipt_
            })
        );

        return (receiptTokenId, actualAmount);
    }

    // ========== CONVERTIBLE DEPOSIT ACTIONS ========== //

    /// @notice Determines the conversion output
    ///
    /// @param  depositor_            The depositor of the position
    /// @param  positionId_           The ID of the position
    /// @param  amount_               The amount of receipt tokens to convert
    /// @param  previousAsset_        Used to validate that the asset is the same across positions (zero if the first position)
    /// @param  previousPeriodMonths_ Used to validate that the period is the same across positions (0 if the first position)
    /// @return convertedTokenOut     The amount of converted tokens
    /// @return currentAsset          The asset of the current position
    /// @return currentPeriodMonths   The period of the current position
    function _previewConvert(
        address depositor_,
        uint256 positionId_,
        uint256 amount_,
        address previousAsset_,
        uint8 previousPeriodMonths_
    )
        internal
        view
        returns (uint256 convertedTokenOut, address currentAsset, uint8 currentPeriodMonths)
    {
        // Validate that the position is valid
        // This will revert if the position is not valid
        DEPOSv1.Position memory position = DEPOS.getPosition(positionId_);

        // Validate that the depositor is the owner of the position
        if (position.owner != depositor_) revert CDF_NotOwner(positionId_);

        // Validate that the position has not expired
        if (block.timestamp >= position.expiry) revert CDF_PositionExpired(positionId_);

        // Validate that the deposit amount is not greater than the remaining deposit
        if (amount_ > position.remainingDeposit) revert CDF_InvalidAmount(positionId_, amount_);

        // Validate that the position supports conversion
        if (position.operator != address(this)) revert CDF_Unsupported(positionId_);

        // Set the asset, or validate
        currentAsset = position.asset;
        currentPeriodMonths = position.periodMonths;
        if (previousAsset_ == address(0)) {
            // Validate that the asset is supported
            if (
                !DEPOSIT_MANAGER
                    .isAssetPeriod(IERC20(currentAsset), currentPeriodMonths, address(this))
                    .isConfigured
            ) revert CDF_InvalidToken(positionId_, currentAsset, currentPeriodMonths);
        } else if (previousAsset_ != currentAsset || previousPeriodMonths_ != currentPeriodMonths) {
            revert CDF_InvalidArgs("multiple assets");
        }

        // The deposit and receipt token have the same decimals, so either can be used
        convertedTokenOut = FullMath.mulDiv(
            amount_, // Scale: deposit token
            _OHM_SCALE,
            position.conversionPrice // Scale: deposit token
        );

        return (convertedTokenOut, currentAsset, currentPeriodMonths);
    }

    /// @inheritdoc IConvertibleDepositFacility
    /// @dev        This function reverts if:
    ///             - The contract is not enabled
    ///             - The length of the positionIds_ array does not match the length of the amounts_ array
    ///             - depositor_ is not the owner of all of the positions
    ///             - Any position is not valid
    ///             - Any position is not a supported asset
    ///             - Any position has a different asset or deposit period
    ///             - Any position has reached the conversion expiry
    ///             - Any conversion amount is greater than the remaining deposit
    ///             - The amount of deposits to convert is 0
    ///             - The converted amount is 0
    function previewConvert(
        address depositor_,
        uint256[] memory positionIds_,
        uint256[] memory amounts_
    ) external view onlyEnabled returns (uint256 receiptTokenIn, uint256 convertedTokenOut) {
        // Make sure the lengths of the arrays are the same
        if (positionIds_.length != amounts_.length) revert CDF_InvalidArgs("array length");

        address asset;
        uint8 periodMonths;
        for (uint256 i; i < positionIds_.length; ++i) {
            uint256 positionId = positionIds_[i];
            uint256 amount = amounts_[i];

            receiptTokenIn += amount;

            (
                uint256 previewConvertOut,
                address currentAsset,
                uint8 currentPeriodMonths
            ) = _previewConvert(depositor_, positionId, amount, asset, periodMonths);
            convertedTokenOut += previewConvertOut;
            asset = currentAsset;
            periodMonths = currentPeriodMonths;
        }

        // If the amount is 0, revert
        if (receiptTokenIn == 0) revert CDF_InvalidArgs("amount");

        // If the converted amount is 0, revert
        if (convertedTokenOut == 0) revert CDF_InvalidArgs("converted amount");

        return (receiptTokenIn, convertedTokenOut);
    }

    /// @inheritdoc IConvertibleDepositFacility
    /// @dev        This function reverts if:
    ///             - The contract is not enabled
    ///             - No positions are provided
    ///             - The length of the positionIds_ array does not match the length of the amounts_ array
    ///             - The caller is not the owner of all of the positions
    ///             - Any position is not valid
    ///             - Any position is not a supported asset
    ///             - Any position has a different asset or deposit period
    ///             - Any position has reached the conversion expiry
    ///             - Any position has a conversion amount greater than the remaining deposit
    ///             - The amount of deposits to convert is 0
    ///             - The converted amount is 0
    function convert(
        uint256[] memory positionIds_,
        uint256[] memory amounts_,
        bool wrappedReceipt_
    )
        external
        nonReentrant
        onlyEnabled
        returns (uint256 receiptTokenIn, uint256 convertedTokenOut)
    {
        if (positionIds_.length == 0) revert CDF_InvalidArgs("no positions");

        // Make sure the lengths of the arrays are the same
        if (positionIds_.length != amounts_.length) revert CDF_InvalidArgs("array length");

        address asset;
        uint8 periodMonths;
        for (uint256 i; i < positionIds_.length; ++i) {
            uint256 positionId = positionIds_[i];
            uint256 depositAmount = amounts_[i];

            receiptTokenIn += depositAmount;

            (
                uint256 previewConvertOut,
                address currentAsset,
                uint8 currentPeriodMonths
            ) = _previewConvert(msg.sender, positionId, depositAmount, asset, periodMonths);
            convertedTokenOut += previewConvertOut;
            asset = currentAsset;
            periodMonths = currentPeriodMonths;

            // Update the position
            DEPOS.setRemainingDeposit(
                positionId,
                DEPOS.getPosition(positionId).remainingDeposit - depositAmount
            );
        }

        // Withdraw the underlying asset and deposit into the treasury
        // The actual amount withdrawn may differ from `receiptTokenIn` by a few wei,
        // but will not materially affect the amount of OHM that is minted when converting.
        // Additionally, given that the amount is composed of multiple positions
        // (each with potentially different conversion prices), it is not trivial to
        // re-calculate `convertedTokenOut` with the actual amount.
        DEPOSIT_MANAGER.withdraw(
            IDepositManager.WithdrawParams({
                asset: IERC20(asset),
                depositPeriod: periodMonths,
                depositor: msg.sender,
                recipient: address(TRSRY),
                amount: receiptTokenIn,
                isWrapped: wrappedReceipt_
            })
        );

        // Mint OHM to the owner/caller
        // No need to check if `convertedTokenOut` is 0, as MINTR will revert
        MINTR.increaseMintApproval(address(this), convertedTokenOut);
        MINTR.mintOhm(msg.sender, convertedTokenOut);

        // Emit event
        emit ConvertedDeposit(asset, msg.sender, periodMonths, receiptTokenIn, convertedTokenOut);

        return (receiptTokenIn, convertedTokenOut);
    }

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

    /// @inheritdoc IConvertibleDepositFacility
    /// @dev        This returns the value from DepositManager.maxClaimYield(), which is a theoretical value.
    function previewClaimYield(IERC20 asset_) public view returns (uint256 yieldAssets) {
        yieldAssets = DEPOSIT_MANAGER.maxClaimYield(asset_, address(this));
        return yieldAssets;
    }

    /// @inheritdoc IConvertibleDepositFacility
    function claimYield(IERC20 asset_) public returns (uint256) {
        // Determine the yield
        uint256 previewedYield = previewClaimYield(asset_);

        return claimYield(asset_, previewedYield);
    }

    /// @inheritdoc IConvertibleDepositFacility
    /// @dev        This function mainly serves as a backup for claiming protocol yield, in case the max yield cannot be claimed.
    function claimYield(IERC20 asset_, uint256 amount_) public returns (uint256) {
        // If disabled, don't do anything
        if (!isEnabled) return 0;

        // Skip if there is no yield to claim
        if (amount_ == 0) return 0;

        // Claim the yield
        // This will revert if the asset is not supported, or the receipt token becomes insolvent
        // The value returned can also be zero
        uint256 actualYield = DEPOSIT_MANAGER.claimYield(asset_, address(TRSRY), amount_);

        // Emit the event
        emit ClaimedYield(address(asset_), actualYield);

        return actualYield;
    }

    /// @inheritdoc IConvertibleDepositFacility
    function claimAllYield() external {
        // Get the assets
        IERC20[] memory assets = DEPOSIT_MANAGER.getConfiguredAssets();

        // Iterate over the deposit assets
        for (uint256 i; i < assets.length; ++i) {
            // Claim the yield
            claimYield(assets[i]);
        }
    }

    // ========== VIEW FUNCTIONS ========== //

    /// @inheritdoc IConvertibleDepositFacility
    function convertedToken() external view returns (address) {
        return address(MINTR.ohm());
    }

    // ========== PERIODIC TASKS ========== //

    /// @inheritdoc IPeriodicTask
    /// @dev        This function reverts if:
    ///             - The caller is not authorized
    ///
    ///             Notes:
    ///             - If disabled, nothing is done
    ///             - This will attempt to claim yield for all configured assets
    ///             - If the claimAllYield function fails for any asset, an event will be emitted instead of reverting
    function execute() external onlyRole(HEART_ROLE) {
        // Don't do anything if disabled
        if (!isEnabled) return;

        try this.claimAllYield() {
            // Do nothing
        } catch {
            // This avoids the periodic task from failing loudly, as the claimAllYield function is not critical to the system
            emit ClaimAllYieldFailed();
        }
    }

    // ========== ERC165 ========== //

    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(BaseDepositFacility, IPeriodicTask) returns (bool) {
        return
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IConvertibleDepositFacility).interfaceId ||
            interfaceId == type(IPeriodicTask).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}
/// forge-lint: disable-end(mixed-case-function, mixed-case-variable)
"
    },
    "src/interfaces/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

// Imported from forge-std

/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
    /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
    /// is the new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the amount of tokens owned by `account`.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Moves `amount` tokens from the caller's account to `to`.
    function transfer(address to, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that `spender` is allowed
    /// to spend on behalf of `owner`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
    /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
    /// `amount` is then deducted from the caller's allowance.
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token.
    function symbol() external view returns (string memory);

    /// @notice Returns the decimals places of the token.
    function decimals() external view returns (uint8);
}
"
    },
    "dependencies/openzeppelin-new-5.3.0/contracts/interfaces/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";
"
    },
    "src/policies/interfaces/deposits/IConvertibleDepositFacility.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @title  IConvertibleDepositFacility
/// @notice Interface for a contract that can perform functions related to convertible deposit (CD) tokens
interface IConvertibleDepositFacility {
    // ========== EVENTS ========== //

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

    event ConvertedDeposit(
        address indexed asset,
        address indexed depositor,
        uint8 periodMonths,
        uint256 depositAmount,
        uint256 convertedAmount
    );

    event ClaimedYield(address indexed asset, uint256 amount);

    event ClaimAllYieldFailed();

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

    error CDF_InvalidArgs(string reason_);

    error CDF_NotOwner(uint256 positionId_);

    error CDF_PositionExpired(uint256 positionId_);

    error CDF_InvalidAmount(uint256 positionId_, uint256 amount_);

    error CDF_InvalidToken(uint256 positionId_, address token_, uint8 periodMonths_);

    error CDF_Unsupported(uint256 positionId_);

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

    /// @notice Parameters for the {createPosition} function
    ///
    /// @param asset             The address of the asset
    /// @param periodMonths      The period of the deposit
    /// @param depositor         The address to create the position for
    /// @param amount            The amount of asset to deposit
    /// @param conversionPrice   The amount of asset tokens required to receive 1 OHM (scale: asset token decimals)
    /// @param wrapPosition      Whether the position should be wrapped
    /// @param wrapReceipt       Whether the receipt token should be wrapped
    struct CreatePositionParams {
        IERC20 asset;
        uint8 periodMonths;
        address depositor;
        uint256 amount;
        uint256 conversionPrice;
        bool wrapPosition;
        bool wrapReceipt;
    }

    // ========== CONVERTIBLE DEPOSIT ACTIONS ========== //

    /// @notice Creates a convertible deposit position
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the asset is supported
    ///         - Validating that the caller has the correct role
    ///         - Depositing the asset
    ///         - Minting the receipt token
    ///         - Creating a new position in the DEPOS module
    ///         - Emitting an event
    ///
    /// @param  params_             The parameters for the position creation
    /// @return positionId          The ID of the new position
    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);

    /// @notice Converts receipt tokens to OHM before conversion expiry
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller is the owner of all of the positions
    ///         - Validating that the token in the position is a supported receipt token
    ///         - Validating that all of the positions are valid
    ///         - Validating that the conversion expiry for all of the positions has not passed
    ///         - Burning the receipt tokens
    ///         - Minting OHM to `account_`
    ///         - Transferring the deposit token to the treasury
    ///         - Emitting an event
    ///
    /// @param  positionIds_        An array of position ids that will be converted
    /// @param  amounts_            An array of amounts of receipt tokens to convert
    /// @param  wrappedReceipt_     Whether the receipt tokens to use are wrapped as ERC20s
    /// @return receiptTokenIn      The total amount of receipt tokens converted
    /// @return convertedTokenOut   The amount of OHM minted during conversion
    function convert(
        uint256[] memory positionIds_,
        uint256[] memory amounts_,
        bool wrappedReceipt_
    ) external returns (uint256 receiptTokenIn, uint256 convertedTokenOut);

    /// @notice Preview the amount of receipt tokens and OHM that would be converted
    /// @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
    ///         - Validating that the conversion expiry for all of the positions has not passed
    ///         - Returning the total amount of receipt tokens and OHM that would be converted
    ///
    /// @param  account_            The address to preview the conversion for
    /// @param  positionIds_        An array of position ids that will be converted
    /// @param  amounts_            An array of amounts of receipt tokens to convert
    /// @return receiptTokenIn      The total amount of receipt tokens converted
    /// @return convertedTokenOut   The amount of OHM minted during conversion
    function previewConvert(
        address account_,
        uint256[] memory positionIds_,
        uint256[] memory amounts_
    ) external view returns (uint256 receiptTokenIn, uint256 convertedTokenOut);

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

    /// @notice Preview the amount of yield that would be claimed for the given asset
    ///
    /// @param  asset_          The address of the asset
    /// @return assets          The amount of assets that would be claimed
    function previewClaimYield(IERC20 asset_) external view returns (uint256 assets);

    /// @notice Claim the yield accrued for the given asset
    ///
    /// @param  asset_          The address of the asset
    /// @return assets          The amount of assets that were claimed
    function claimYield(IERC20 asset_) external returns (uint256 assets);

    /// @notice Claim the yield accrued for the given asset
    ///
    /// @param  asset_          The address of the asset
    /// @param  amount_         The amount to claim
    /// @return assets          The amount of assets that were claimed
    function claimYield(IERC20 asset_, uint256 amount_) external returns (uint256 assets);

    /// @notice Claim the yield accrued for all assets and deposit periods
    function claimAllYield() external;

    // ========== VIEW FUNCTIONS ========== //

    /// @notice The address of the token that is converted to by the facility
    function convertedToken() external view returns (address);
}
"
    },
    "src/policies/interfaces/deposits/IDepositManager.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IERC20} from "src/interfaces/IERC20.sol";
import {IERC4626} from "src/interfaces/IERC4626.sol";
import {IAssetManager} from "src/bases/interfaces/IAssetManager.sol";
import {IReceiptTokenManager} from "src/policies/interfaces/deposits/IReceiptTokenManager.sol";

/// @title  Deposit Manager
/// @notice Defines an interface for a policy that manages deposits on behalf of other contracts. It is meant to be used by the facilities, and is not an end-user policy.
///
///         Key terms for the contract:
///         - Asset: an ERC20 asset that can be deposited into the contract
///         - Asset vault: an optional ERC4626 vault that assets are deposited into
///         - Asset period: the combination of an asset and deposit period
interface IDepositManager is IAssetManager {
    // ========== EVENTS ========== //

    event OperatorYieldClaimed(
        address indexed asset,
        address indexed depositor,
        address indexed operator,
        uint256 amount
    );

    // Asset Configuration Events
    event OperatorNameSet(address indexed operator, string name);

    event AssetPeriodConfigured(
        uint256 indexed receiptTokenId,
        address indexed asset,
        address indexed operator,
        uint8 depositPeriod
    );

    event AssetPeriodEnabled(
        uint256 indexed receiptTokenId,
        address indexed asset,
        address indexed operator,
        uint8 depositPeriod
    );

    event AssetPeriodDisabled(
        uint256 indexed receiptTokenId,
        address indexed asset,
        address indexed operator,
        uint8 depositPeriod
    );

    event TokenRescued(address indexed token, uint256 amount);

    // Borrowing Events
    event BorrowingWithdrawal(
        address indexed asset,
        address indexed operator,
        address indexed recipient,
        uint256 amount
    );

    event BorrowingRepayment(
        address indexed asset,
        address indexed operator,
        address indexed payer,
        uint256 amount
    );

    event BorrowingDefault(
        address indexed asset,
        address indexed operator,
        address indexed payer,
        uint256 amount
    );

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

    error DepositManager_InvalidParams(string reason);

    /// @notice Error if the action would leave the contract insolvent (liabilities > assets + borrowed)
    ///
    /// @param asset                    The address of the underlying asset
    /// @param requiredAssets           The quantity of asset liabilities
    /// @param depositedSharesInAssets  The quantity of assets that the deposited shares represent
    /// @param borrowedAmount           The quantity of assets that are currently borrowed
    error DepositManager_Insolvent(
        address asset,
        uint256 requiredAssets,
        uint256 depositedSharesInAssets,
        uint256 borrowedAmount
    );

    error DepositManager_ZeroAddress();

    error DepositManager_OutOfBounds();

    error DepositManager_CannotRescueAsset(address token);

    // Asset Configuration Errors
    error DepositManager_OperatorNameNotSet(address operator);

    error DepositManager_OperatorNameSet(address operator);

    error DepositManager_OperatorNameInvalid();

    error DepositManager_OperatorNameInUse(string name);

    error DepositManager_InvalidAssetPeriod(address asset, uint8 depositPeriod, address operator);

    error DepositManager_AssetPeriodExists(address asset, uint8 depositPeriod, address operator);

    error DepositManager_AssetPeriodEnabled(address asset, uint8 depositPeriod, address operator);

    error DepositManager_AssetPeriodDisabled(address asset, uint8 depositPeriod, address operator);

    // Borrowing Errors
    error DepositManager_BorrowingLimitExceeded(
        address asset,
        address operator,
        uint256 requested,
        uint256 available
    );

    error DepositManager_BorrowedAmountExceeded(
        address asset,
        address operator,
        uint256 amount,
        uint256 borrowed
    );

    // ========== STRUCTS ========== //

    /// @notice Parameters for the {deposit} function
    ///
    /// @param asset           The underlying ERC20 asset
    /// @param depositPeriod   The deposit period, in months
    /// @param depositor       The depositor
    /// @param amount          The amount to deposit
    /// @param shouldWrap      Whether the receipt token should be wrapped
    struct DepositParams {
        IERC20 asset;
        uint8 depositPeriod;
        address depositor;
        uint256 amount;
        bool shouldWrap;
    }

    /// @notice Parameters for the {withdraw} function
    ///
    /// @param asset            The underlying ERC20 asset
    /// @param depositPeriod    The deposit period, in months
    /// @param depositor        The depositor that is holding the receipt tokens
    /// @param recipient        The recipient of the withdrawn asset
    /// @param amount           The amount to withdraw
    /// @param isWrapped        Whether the receipt token is wrapped
    struct WithdrawParams {
        IERC20 asset;
        uint8 depositPeriod;
        address depositor;
        address recipient;
        uint256 amount;
        bool isWrapped;
    }

    /// @notice An asset period configuration, representing an asset and period combination
    ///
    /// @param isEnabled       Whether the asset period is enabled for new deposits
    /// @param depositPeriod   The deposit period, in months
    /// @param asset           The underlying ERC20 asset
    /// @param operator        The operator that can issue this receipt token
    struct AssetPeriod {
        bool isEnabled;
        uint8 depositPeriod;
        address asset;
        address operator;
    }

    /// @notice Status of an asset period
    ///
    /// @param isConfigured    Whether the asset period is configured
    /// @param isEnabled       Whether the asset period is enabled for new deposits
    struct AssetPeriodStatus {
        bool isConfigured;
        bool isEnabled;
    }

    /// @notice Parameters for borrowing withdrawal operations
    ///
    /// @param asset           The underlying ERC20 asset
    /// @param recipient       The recipient of the borrowed funds
    /// @param amount          The amount to borrow
    struct BorrowingWithdrawParams {
        IERC20 asset;
        address recipient;
        uint256 amount;
    }

    /// @notice Parameters for borrowing repayment operations
    ///
    /// @param asset        The underlying ERC20 asset
    /// @param payer        The address making the repayment
    /// @param amount       The amount of principal to repay
    /// @param maxAmount    The maximum amount that can be repaid
    struct BorrowingRepayParams {
        IERC20 asset;
        address payer;
        uint256 amount;
        uint256 maxAmount;
    }

    /// @notice Parameters for borrowing default operations
    ///
    /// @param asset           The underlying ERC20 asset
    /// @param depositPeriod   The deposit period, in months
    /// @param payer           The address making the default
    /// @param amount          The amount to default
    struct BorrowingDefaultParams {
        IERC20 asset;
        uint8 depositPeriod;
        address payer;
        uint256 amount;
    }

    // ========== BORROWING FUNCTIONS ========== //

    /// @notice Borrows funds from deposits
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Validating borrowing limits and capacity
    ///         - Transferring the underlying asset from the contract to the recipient
    ///         - Updating borrowing state
    ///         - Checking solvency
    ///
    /// @param  params_         The parameters for the borrowing withdrawal
    /// @return actualAmount    The quantity of underlying assets transferred to the recipient
    function borrowingWithdraw(
        BorrowingWithdrawParams calldata params_
    ) external returns (uint256 actualAmount);

    /// @notice Repays borrowed funds
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Transferring the underlying asset from the payer to the contract
    ///         - Updating borrowing state
    ///         - Checking solvency
    ///
    /// @param  params_         The parameters for the borrowing repayment
    /// @return actualAmount    The quantity of underlying assets received from the payer
    function borrowingRepay(
        BorrowingRepayParams calldata params_
    ) external returns (uint256 actualAmount);

    /// @notice Defaults on a borrowed amount
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Burning the receipt tokens from the payer for the default amount
    ///         - Updating borrowing state
    ///         - Updating liabilities
    function borrowingDefault(BorrowingDefaultParams calldata params_) external;

    /// @notice Gets the current borrowed amount for an operator
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  operator_       The address of the operator
    /// @return borrowed        The current borrowed amount for the operator
    function getBorrowedAmount(
        IERC20 asset_,
        address operator_
    ) external view returns (uint256 borrowed);

    /// @notice Gets the available borrowing capacity for an operator
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  operator_       The address of the operator
    /// @return capacity        The available borrowing capacity for the operator
    function getBorrowingCapacity(
        IERC20 asset_,
        address operator_
    ) external view returns (uint256 capacity);

    // ========== DEPOSIT/WITHDRAW FUNCTIONS ========== //

    /// @notice Deposits the given amount of the underlying asset in exchange for a receipt token
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Transferring the underlying asset from the depositor to the contract
    ///         - Minting the receipt token to the depositor
    ///         - Updating the amount of deposited funds
    ///
    /// @param  params_         The parameters for the deposit
    /// @return receiptTokenId  The ID of the receipt token
    /// @return actualAmount    The quantity of receipt tokens minted to the depositor
    function deposit(
        DepositParams calldata params_
    ) external returns (uint256 receiptTokenId, uint256 actualAmount);

    /// @notice Returns the maximum yield that can be claimed for an asset and operator pair
    ///
    /// @param  asset_        The address of the underlying asset
    /// @param  operator_     The address of the operator
    /// @return yieldAssets   The amount of yield that can be claimed
    function maxClaimYield(
        IERC20 asset_,
        address operator_
    ) external view returns (uint256 yieldAssets);

    /// @notice Claims the yield from the underlying asset
    ///         This does not burn receipt tokens, but should reduce the amount of shares the caller has in the vault.
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Transferring the underlying asset from the contract to the recipient
    ///         - Updating the amount of deposited funds
    ///         - Checking solvency
    ///
    /// @param  asset_        The address of the underlying asset
    /// @param  recipient_    The recipient of the claimed yield
    /// @param  amount_       The amount to claim yield for
    /// @return actualAmount  The quantity of underlying assets transferred to the recipient
    function claimYield(
        IERC20 asset_,
        address recipient_,
        uint256 amount_
    ) external returns (uint256 actualAmount);

    /// @notice Withdraws the given amount of the underlying asset
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Burning the receipt token
    ///         - Transferring the underlying asset from the contract to the recipient
    ///         - Updating the amount of deposited funds
    ///
    /// @param  params_         The parameters for the withdrawal
    /// @return actualAmount    The quantity of underlying assets transferred to the recipient
    function withdraw(WithdrawParams calldata params_) external returns (uint256 actualAmount);

    /// @notice Returns the liabilities for an asset and operator pair
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  operator_       The address of the operator
    /// @return liabilities     The quantity of assets that the contract is custodying for the operator's depositors
    function getOperatorLiabilities(
        IERC20 asset_,
        address operator_
    ) external view returns (uint256 liabilities);

    // ========== OPERATOR NAMES ========== //

    /// @notice Sets the name of an operator. This is included in the name and symbol of receipt tokens.
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Setting the operator name
    ///         - Emitting an event
    function setOperatorName(address operator_, string calldata name_) external;

    /// @notice Returns the name of an operator
    ///
    /// @param  operator_   The address of the operator
    /// @return name        The name of the operator or an empty string
    function getOperatorName(address operator_) external view returns (string memory name);

    // ========== DEPOSIT CONFIGURATIONS ========== //

    /// @notice Adds a new asset
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Configuring the asset
    ///         - Emitting an event
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  vault_          The address of the ERC4626 vault to deposit the asset into (or the zero address)
    /// @param  depositCap_     The deposit cap of the asset
    /// @param  minimumDeposit_ The minimum deposit amount for the asset
    function addAsset(
        IERC20 asset_,
        IERC4626 vault_,
        uint256 depositCap_,
        uint256 minimumDeposit_
    ) external;

    /// @notice Sets the deposit cap for an asset
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Setting the deposit cap for the asset
    ///         - Emitting an event
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  depositCap_     The deposit cap to set for the asset
    function setAssetDepositCap(IERC20 asset_, uint256 depositCap_) external;

    /// @notice Sets the minimum deposit for an asset
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Setting the minimum deposit for the asset
    ///         - Emitting an event
    ///
    /// @param  asset_           The address of the underlying asset
    /// @param  minimumDeposit_  The minimum deposit to set for the asset
    function setAssetMinimumDeposit(IERC20 asset_, uint256 minimumDeposit_) external;

    /// @notice Adds a new asset period
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Creating a new receipt token
    ///         - Emitting an event
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  depositPeriod_  The deposit period, in months
    /// @param  operator_       The address of the operator
    /// @return receiptTokenId  The ID of the new receipt token
    function addAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) external returns (uint256 receiptTokenId);

    /// @notice Disables an asset period, which prevents new deposits
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Disabling the asset period
    ///         - Emitting an event
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  depositPeriod_  The deposit period, in months
    /// @param  operator_       The address of the operator
    function disableAssetPeriod(IERC20 asset_, uint8 depositPeriod_, address operator_) external;

    /// @notice Enables an asset period, which allows new deposits
    /// @dev    The implementing contract is expected to handle the following:
    ///         - Validating that the caller has the correct role
    ///         - Enabling the asset period
    ///         - Emitting an event
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  depositPeriod_  The deposit period, in months
    /// @param  operator_       The address of the operator
    function enableAssetPeriod(IERC20 asset_, uint8 depositPeriod_, address operator_) external;

    /// @notice Returns the asset period for an asset, period and operator
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  depositPeriod_  The deposit period, in months
    /// @param  operator_       The address of the operator
    /// @return configuration   The asset period
    function getAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) external view returns (AssetPeriod memory configuration);

    /// @notice Returns the asset period from a receipt token ID
    ///
    /// @param  tokenId_        The ID of the receipt token
    /// @return configuration   The asset period
    function getAssetPeriod(
        uint256 tokenId_
    ) external view returns (AssetPeriod memory configuration);

    /// @notice Returns whether a deposit asset, period and operator combination are configured
    /// @dev    A asset period that is disabled will not accept further deposits
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  depositPeriod_  The deposit period, in months
    /// @param  operator_       The address of the operator
    /// @return status          The status of the asset period
    function isAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) external view returns (AssetPeriodStatus memory status);

    /// @notice Gets all configured asset periods
    ///
    /// @return assetPeriods    Array of configured asset periods
    function getAssetPeriods() external view returns (AssetPeriod[] memory assetPeriods);

    // ========== RECEIPT TOKEN FUNCTIONS ========== //

    /// @notice Returns the ID of the receipt token for an asset period and operator
    /// @dev    The ID returned is not a guarantee that the asset period is configured or enabled. {isAssetPeriod} should be used for that purpose.
    ///
    /// @param  asset_          The address of the underlying asset
    /// @param  depositPeriod_  The deposit period, in months
    /// @param  operator_       The address of the operator
    /// @return receiptTokenId  The ID of the receipt token
    function getReceiptTokenId(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) external view returns (uint256 receiptTokenId);

    /// @notice Convenience function that returns both receipt token ID and wrapped token address
    ///
    /// @param  asset_          The asset contract
    /// @param  depositPeriod_  The deposit period in months
    /// @param  operator_       The operator address
    /// @return tokenId         The receipt token ID
    /// @return wrappedToken    The address of the wrapped ERC20 token (0x0 if not created yet)
    function getReceiptToken(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) external view returns (uint256 tokenId, address wrappedToken);

    /// @notice Gets the receipt token manager
    ///
    /// @return manager The receipt token manager contract
    function getReceiptTokenManager() external view returns (IReceiptTokenManager manager);

    /// @notice Gets all receipt token IDs owned by this contract
    ///
    /// @return tokenIds Array of receipt token IDs
    function getReceiptTokenIds() external view returns (uint256[] memory tokenIds);
}
"
    },
    "src/modules/DEPOS/IDepositPositionManager.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.15;

/// @title  IDepositPositionManager
/// @notice This interface defines the functions for the DEPOS module.
///         The objective of this module is to track the terms of a deposit position.
interface IDepositPositionManager {
    // ========== DATA STRUCTURES ========== //

    /// @notice Data structure for the terms of a deposit position
    ///
    /// @param  operator                Address of the operator/creator of the position
    /// @param  owner                   Address of the owner of the position
    /// @param  asset                   Address of the asset
    /// @param  periodMonths            The period of the deposit
    /// @param  remainingDeposit        Amount of reserve tokens remaining to be converted
    /// @param  conversionPrice         The amount of asset tokens required to receive 1 OHM (scale: asset token decimals)
    /// @param  expiry                  Timestamp of the position expiry
    /// @param  wrapped                 Whether the term is wrapped
    /// @param  additionalData          Additional data for the position
    struct Position {
        address operator;
        address owner;
        address asset;
        uint8 periodMonths;
        uint256 remainingDeposit;
        uint256 conversionPrice;
        uint48 expiry;
        bool wrapped;
        bytes additionalData;
    }

    /// @notice Parameters for the {mint} function
    ///
    /// @param  owner                   Address of the owner of the position
    /// @param  asset                   Address of the asset
    /// @param  periodMonths            The period of the deposit
    /// @param  remainingDeposit        Amount of reserve tokens remaining to be converted
    /// @param  conversionPrice         The amount of asset tokens required to receive 1 OHM (scale: asset token decimals)
    /// @param  expiry                  Timestamp of the position expiry
    /// @param  wrapPosition            Whether the position should be wrapped
    /// @param  additionalData          Additional data for the position
    struct MintParams {
        address owner;
        address asset;
        uint8 periodMonths;
        uint256 remainingDeposit;
        uint256 conversionPrice;
        uint48 expiry;
        bool wrapPosition;
        bytes additionalData;
    }

    // ========== EVENTS ========== //

    /// @notice Emitted when a position is created
    event PositionCreated(
        uint256 indexed positionId,
        address indexed owner,
        address indexed asset,
        uint8 periodMonths,
        uint256 remainingDeposit,
        uint256 conversionPrice,
        uint48 expiry,
        bool wrapped
    );

    /// @notice Emitted when a position's remaining deposit is updated
    event PositionRemainingDepositUpdated(uint256 indexed positionId, uint256 remainingDeposit);

    /// @notice Emitted when a position's additional data is updated
    event PositionAdditionalDataUpdated(uint256 indexed positionId, bytes additionalData);

    /// @notice Emitted when a position is split
    event PositionSplit(
        uint256 indexed positionId,
        uint256 indexed newPositionId,
        address indexed asset,
        uint8 periodMonths,
        uint256 amount,
        address to,
        bool wrap
    );

    /// @notice Emitted when a position is wrapped
    event PositionWrapped(uint256 indexed positionId);

    /// @notice Emitted when a position is unwrapped
    event PositionUnwrapped(uint256 indexed positionId);

    /// @notice Emitted when the token renderer is set
    event TokenRendererSet(address indexed renderer);

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

    /// @notice Error thrown when the caller is not the owner of the position
    error DEPOS_NotOwner(uint256 positionId_);

    /// @notice Error thrown when the caller is not the operator of the position
    error DEPOS_NotOperator(uint256 positionId_);

    /// @notice Error thrown when an invalid position ID is provided
    error DEPOS_InvalidPositionId(uint256 id_);

    /// @notice Error thrown when a position has already been wrapped
    error DEPOS_AlreadyWrapped(uint256 positionId_);

    /// @notice Error thrown when a position has not been wrapped
    error DEPOS_NotWrapped(uint256 positionId_);

    /// @notice Error thrown when an invalid parameter is provided
    error DEPOS_InvalidParams(string reason_);

    /// @notice Error thrown when a position does not support conversion
    error DEPOS_NotConvertible(uint256 positionId_);

    /// @notice Error thrown when the renderer contract does not implement the required interface
    error DEPOS_InvalidRenderer(address renderer_);

    // ========== WRAPPING ========== //

    /// @notice Wraps a position into an ERC721 token
    ///         This is useful if the position owner wants a tokenized representation of their position. It is functionally equivalent to the position itself.
    ///
    /// @dev    The implementing function should do the following:
    ///         - Validate that the caller is the owner of the position
    ///         - Validate that the position is not already wrapped
    ///         - Mint an ERC721 token to the position owner
    ///
    /// @param  positionId_ The ID of the position to wrap
    function wrap(uint256 positionId_) external;

    /// @notice Unwraps/burns an ERC721 position token
    ///         This is useful if the position owner wants to unwrap their token back into the position.
    ///
    /// @dev    The implementing function should do the following:
    ///         - Validate that the caller is the owner of the position
    ///         - Validate that the position is already wrapped
    ///         - Burn the ERC721 token
    ///
    /// @param  positionId_ The ID of the position to unwrap
    function unwrap(uint256 positionId_) external;

    // ========== POSITION MANAGEMENT =========== //

    /// @notice Creates a new deposit position
    /// @dev    The implementing function should do the following:
    ///         - Validate that the caller is permissioned
    ///         - Validate that the owner is not the zero address
    ///         - Validate that the convertible deposit token is not the zero address
    ///         - Validate that the remaining deposit is greater than 0
    ///         - Validate that the conversion price is greater than 0
    ///         - Validate that the expiry is in the future
    ///         - Create the position record
    ///         - Wrap the position if requested
    ///
    /// @param  params_                     The parameters for the position creation
    /// @return _positionId                 The ID of the new position
    function mint(MintParams calldata params_) external returns (uint256 _positionId);

    /// @notice Updates the remaining deposit of a position
    /// @dev    The implementing function should do the following:
    ///         - Validate that the caller is permissioned
    ///         - Validate that the position ID is valid
    ///         - Update the remaining deposit of the position
    ///
    /// @param  positionId_ The ID of the position to update
    /// @param  amount_     The new amount of the position
    function setRemainingDeposit(uint256 positionId_, uint256 amount_) external;

    /// @notice Updates the additional data of a position
    /// @dev    The implementing function should do the following:
    ///         - Validate that the caller is permissioned
    ///         - Validate that the position ID is valid
    ///         - Update the additional data of the position
    ///
    /// @param  positionId_         The ID of the position to update
    /// @param  additionalData_     The new additional data of the position
    function setAdditionalData(uint256 positionId_, bytes calldata additionalData_) external;

    /// @notice Splits the specified amount of the position into a new position
    ///         This is useful if the position owner wants to split their position into multiple smaller positions.
    /// @dev    The implementing function should do the following:
    ///         - Validate that the caller is the owner of the position
    ///         - Validate that the amount is greater than 0
    ///         - Validate that the amount is less than or equal to the remaining deposit
    ///         - Validate that `to_` is not the zero address
    ///         - Update the remaining deposit of the original position
    ///         - Create the new position record
    ///         - Wrap the new position if requested
    ///
    /// @param  positionId_     The ID of the position to split
    /// @param  amount_         The amount of the position to split
    /// @param  to_             The address to split the position to
    /// @param  wrap_           Whether the new position should be wrapped
    /// @return newPositionId   The ID of the new position
    function split(
        uint256 positionId_,
        uint256 amount_,
        address to_,
        bool wrap_
    ) external returns (uint256 newPositionId);

    // ========== POSITION INFORMATION ========== //

    /// @notice Get the total number of positions
    ///
    /// @return _count The total number of positions
    function getPositionCount() external view returns (uint256 _count);

    /// @notice Get the IDs of all positions for a given user
    ///
    /// @param  user_           The address of the user
    /// @return _positionIds    An array of position IDs
    function getUserPositionIds(
        address user_
    ) external view returns (uint256[] memory _positionIds);

    /// @notice Get the position for a given ID
    ///
    /// @param  positionId_ The ID of the position
    /// @return _position   The position for the given ID
    function getPosition(uint256 positionId_) external view returns (Position memory _position);

    /// @notice Check if a position is expired
    ///
    /// @param  positionId_ The ID of the position
    /// @return _expired    Whether the position is expired
    function isExpired(uint256 positionId_) external view returns (bool _expired);

    /// @notice Check if a position is convertible
    ///
    /// @param  positionId_     The ID of the position
    /// @return _convertible    Whether the position is convertible
    function isConvertible(uint256 positionId_) external view returns (bool _convertible);

    /// @notice Preview the amount of OHM that would be received for a given amount of convertible deposit tokens
    ///
    /// @param  positionId_ The ID of the position
    /// @param  amount_     The amount of convertible deposit tokens to convert
    /// @return _ohmOut     The amount of OHM that would be received
    function previewConvert(
        uint256 positionId_,
        uint256 amount_
    ) external view returns (uint256 _ohmOut);

    // ========== TOKEN URI RENDERER ========== //

    /// @notice Set the token renderer contract
    /// @dev    The implementing function should do the following:
    ///         - Validate that the caller is permissioned
    ///         - Validate that the renderer contract implements the required interface
    ///         - Set the renderer contract
    ///         - Emit an event
    ///
    /// @param  renderer_ The address of the renderer contract
    function setTokenRenderer(address renderer_) external;

    /// @notice Get the current token renderer contract
    ///
    /// @return _renderer The address of the current renderer contract (or zero address if not set)
    function getTokenRenderer() external view returns (address _renderer);
}
"
    },
    "src/interfaces/IPeriodicTask.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @title  IPeriodicTask
/// @notice Interface for a contract that can perform a task at a specified interval
interface IPeriodicTask {
    // ========== FUNCTIONS ========== //

    /// @notice Executes the periodic task
    /// @dev    Guidelines for implementing functions:
    /// @dev    - The implementing function is responsible for checking if the task is due to be executed.
    /// @dev    - The implementing function should avoid reverting, as that would cause the calling contract to revert.
    /// @dev    - The implementing function should be protected by a role check for the "heart" role.
    function execute() external;

    /// @notice ERC165 interface support
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
    },
    "src/libraries/FullMath.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = a * b
            // Compute the product mod 2**256 and mod 2**256 - 1
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2**256 + prod0
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod

Tags:
ERC20, ERC721, ERC165, Multisig, Mintable, Burnable, Non-Fungible, Swap, Yield, Voting, Timelock, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xebde552d851dd6dfd3d360c596d3f4af6e5f9678|verified:true|block:23747533|tx:0x06e13957d53a4a25f1abb71f278023d7aeb0630611875a4fe0734df24a9e9fbd|first_check:1762528435

Submitted on: 2025-11-07 16:13:56

Comments

Log in to comment.

No comments yet.