DepositManager

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/DepositManager.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0
/// forge-lint: disable-start(asm-keccak256, mixed-case-function)
pragma solidity >=0.8.20;

// Interfaces
import {IERC20} from "src/interfaces/IERC20.sol";
import {IDepositManager} from "src/policies/interfaces/deposits/IDepositManager.sol";
import {IReceiptTokenManager} from "src/policies/interfaces/deposits/IReceiptTokenManager.sol";
import {IERC4626} from "src/interfaces/IERC4626.sol";
import {IERC165} from "@openzeppelin-5.3.0/interfaces/IERC165.sol";

// Libraries
import {ERC20} from "@solmate-6.2.0/tokens/ERC20.sol";
import {EnumerableSet} from "@openzeppelin-5.3.0/utils/structs/EnumerableSet.sol";
import {TransferHelper} from "src/libraries/TransferHelper.sol";

// Bophades
import {Kernel, Keycode, Permissions, Policy, toKeycode} from "src/Kernel.sol";
import {ROLESv1} from "src/modules/ROLES/OlympusRoles.sol";
import {PolicyEnabler} from "src/policies/utils/PolicyEnabler.sol";
import {BaseAssetManager} from "src/bases/BaseAssetManager.sol";
import {ReceiptTokenManager} from "src/policies/deposits/ReceiptTokenManager.sol";

/// @title Deposit Manager
/// @notice This policy manages deposits and withdrawals for Olympus protocol contracts
/// @dev    Key Features:
///         - ERC6909 receipt tokens with optional ERC20 wrapping, using ReceiptTokenManager
///         - Operator isolation preventing cross-operator fund access
///         - Borrowing functionality
///         - Configurable reclaim rates for risk management
contract DepositManager is Policy, PolicyEnabler, IDepositManager, BaseAssetManager {
    using TransferHelper for ERC20;
    using EnumerableSet for EnumerableSet.UintSet;

    // ========== CONSTANTS ========== //

    /// @notice The role that is allowed to deposit and withdraw funds
    bytes32 public constant ROLE_DEPOSIT_OPERATOR = "deposit_operator";

    /// @notice The receipt token manager for creating receipt tokens
    ReceiptTokenManager internal immutable _RECEIPT_TOKEN_MANAGER;

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

    /// @notice Maps asset liabilities key to the number of receipt tokens that have been minted
    /// @dev    This is used to ensure that the receipt tokens are solvent
    ///         As with the BaseAssetManager, deposited asset tokens with different deposit periods are co-mingled.
    mapping(bytes32 key => uint256 receiptTokenSupply) internal _assetLiabilities;

    /// @notice Maps token ID to the asset period
    mapping(uint256 tokenId => AssetPeriod) internal _assetPeriods;

    /// @notice Set of token IDs that this DepositManager owns
    EnumerableSet.UintSet internal _ownedTokenIds;

    /// @notice Constant equivalent to 100%
    uint16 public constant ONE_HUNDRED_PERCENT = 100e2;

    /// @notice Maps operator address to its name
    mapping(address operator => bytes3 name) internal _operatorToName;

    /// @notice A set of operator names
    /// @dev    This contains unique values
    mapping(bytes3 name => bool isRegistered) internal _operatorNames;

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

    /// @notice Maps asset-operator key to current borrowed amounts
    /// @dev    The key is the keccak256 of the asset address and the operator address
    mapping(bytes32 key => uint256 borrowedAmount) internal _borrowedAmounts;

    // ========== MODIFIERS ========== //

    function _onlyAssetPeriodExists(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) internal view {
        uint256 tokenId = _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
            address(this),
            asset_,
            depositPeriod_,
            operator_
        );
        if (address(_assetPeriods[tokenId].asset) == address(0)) {
            revert DepositManager_InvalidAssetPeriod(address(asset_), depositPeriod_, operator_);
        }
    }

    /// @notice Reverts if the asset period is not configured
    modifier onlyAssetPeriodExists(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) {
        _onlyAssetPeriodExists(asset_, depositPeriod_, operator_);
        _;
    }

    function _onlyAssetPeriodEnabled(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) internal view {
        uint256 tokenId = _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
            address(this),
            asset_,
            depositPeriod_,
            operator_
        );
        AssetPeriod memory assetPeriod = _assetPeriods[tokenId];
        if (assetPeriod.asset == address(0)) {
            revert DepositManager_InvalidAssetPeriod(address(asset_), depositPeriod_, operator_);
        }
        if (!assetPeriod.isEnabled) {
            revert DepositManager_AssetPeriodDisabled(address(asset_), depositPeriod_, operator_);
        }
    }

    /// @notice Reverts if the asset period is not enabled
    modifier onlyAssetPeriodEnabled(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) {
        _onlyAssetPeriodEnabled(asset_, depositPeriod_, operator_);
        _;
    }

    // ========== CONSTRUCTOR ========== //

    constructor(address kernel_, address tokenManager_) Policy(Kernel(kernel_)) {
        // Validate that the token manager implements IReceiptTokenManager
        if (!IERC165(tokenManager_).supportsInterface(type(IReceiptTokenManager).interfaceId)) {
            revert DepositManager_InvalidParams("token manager");
        }

        _RECEIPT_TOKEN_MANAGER = ReceiptTokenManager(tokenManager_);

        // Disabled by default by PolicyEnabler
    }

    // ========== Policy Configuration ========== //

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

        ROLES = ROLESv1(getModuleAddress(dependencies[0]));
    }

    /// @inheritdoc Policy
    function requestPermissions()
        external
        view
        override
        returns (Permissions[] memory permissions)
    {}

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

        return (major, minor);
    }

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

    /// @inheritdoc IDepositManager
    /// @dev        This function is only callable by addresses with the deposit operator role
    ///
    ///             The actions of the calling deposit operator are restricted to its own namespace, preventing the operator from accessing funds of other operators.
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the deposit operator role
    ///             - The asset/deposit period/operator combination is not enabled
    ///             - The deposit amount is below the minimum deposit requirement
    ///             - The deposit would exceed the asset's deposit cap for the operator
    ///             - The depositor has not approved the DepositManager to spend the asset tokens
    ///             - The depositor has insufficient asset token balance
    ///             - The asset is a fee-on-transfer token
    ///             - Zero shares would be received from the vault
    function deposit(
        DepositParams calldata params_
    )
        external
        onlyEnabled
        onlyRole(ROLE_DEPOSIT_OPERATOR)
        onlyAssetPeriodEnabled(params_.asset, params_.depositPeriod, msg.sender)
        returns (uint256 receiptTokenId, uint256 actualAmount)
    {
        // Deposit into vault
        // This will revert if the asset is not configured
        // This takes place before any state changes to avoid ERC777 re-entrancy
        (actualAmount, ) = _depositAsset(
            params_.asset,
            params_.depositor,
            params_.amount,
            true // Enforce minimum deposit
        );

        // Mint the receipt token to the caller
        receiptTokenId = _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
            address(this),
            params_.asset,
            params_.depositPeriod,
            msg.sender
        );
        _RECEIPT_TOKEN_MANAGER.mint(
            params_.depositor,
            receiptTokenId,
            actualAmount,
            params_.shouldWrap
        );

        // Update the asset liabilities for the caller (operator)
        _assetLiabilities[_getAssetLiabilitiesKey(params_.asset, msg.sender)] += actualAmount;

        return (receiptTokenId, actualAmount);
    }

    /// @inheritdoc IDepositManager
    /// @dev        The actions of the calling deposit operator are restricted to its own namespace, preventing the operator from accessing funds of other operators.
    ///
    ///             Note that the returned value is a theoretical maximum. The theoretical value may not be accurate or possible due to rounding and other behaviours in an ERC4626 vault.
    function maxClaimYield(IERC20 asset_, address operator_) external view returns (uint256) {
        (, uint256 depositedSharesInAssets) = getOperatorAssets(asset_, operator_);
        bytes32 assetLiabilitiesKey = _getAssetLiabilitiesKey(asset_, operator_);
        uint256 operatorLiabilities = _assetLiabilities[assetLiabilitiesKey];
        uint256 borrowedAmount = _borrowedAmounts[assetLiabilitiesKey];

        // Avoid reverting
        // Adjust by 1 to account for the different behaviour in ERC4626.previewRedeem and ERC4626.previewWithdraw, which could leave the receipt token insolvent
        if (depositedSharesInAssets + borrowedAmount < operatorLiabilities + 1) return 0;

        return depositedSharesInAssets + borrowedAmount - operatorLiabilities - 1;
    }

    /// @inheritdoc IDepositManager
    /// @dev        Notes:
    ///             - This function is only callable by addresses with the deposit operator role
    ///             - The actions of the calling deposit operator are restricted to its own namespace, preventing the operator from accessing funds of other operators.
    ///             - Given a low enough amount, the actual amount withdrawn may be 0. This function will not revert in such a case.
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the deposit operator role
    ///             - The asset is not configured in BaseAssetManager
    ///             - The operator becomes insolvent after the withdrawal (assets + borrowed < liabilities)
    function claimYield(
        IERC20 asset_,
        address recipient_,
        uint256 amount_
    )
        external
        onlyEnabled
        onlyRole(ROLE_DEPOSIT_OPERATOR)
        onlyConfiguredAsset(asset_)
        returns (uint256 actualAmount)
    {
        // Withdraw the funds from the vault
        // The value returned can also be zero
        (, actualAmount) = _withdrawAsset(asset_, recipient_, amount_);

        // The receipt token supply is not adjusted here, as there is no minting/burning of receipt tokens

        // Validate operator solvency after withdrawal
        _validateOperatorSolvency(asset_, msg.sender);

        // Emit an event
        emit OperatorYieldClaimed(address(asset_), recipient_, msg.sender, actualAmount);

        return actualAmount;
    }

    /// @inheritdoc IDepositManager
    /// @dev        Notes:
    ///             - This function is only callable by addresses with the deposit operator role
    ///             - The actions of the calling deposit operator are restricted to its own namespace, preventing the operator from accessing funds of other operators.
    ///             - Given a low enough amount, the actual amount withdrawn may be 0. This function will not revert in such a case.
    ///
    ///             This function will revert if:
    ///             - The contract is not enabled
    ///             - The caller does not have the deposit operator role
    ///             - The recipient is the zero address
    ///             - The asset/deposit period/operator combination is not configured
    ///             - The depositor has insufficient receipt token balance
    ///             - For wrapped tokens: depositor has not approved ReceiptTokenManager to spend the wrapped ERC20 token
    ///             - For unwrapped tokens: depositor has not approved the caller to spend ERC6909 tokens
    ///             - The operator becomes insolvent after the withdrawal (assets + borrowed < liabilities)
    function withdraw(
        WithdrawParams calldata params_
    ) external onlyEnabled onlyRole(ROLE_DEPOSIT_OPERATOR) returns (uint256 actualAmount) {
        // Validate that the recipient is not the zero address
        if (params_.recipient == address(0)) revert DepositManager_ZeroAddress();

        // Burn the receipt token from the depositor
        // Will revert if the asset configuration is not valid/invalid receipt token ID
        _RECEIPT_TOKEN_MANAGER.burn(
            params_.depositor,
            _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
                address(this),
                params_.asset,
                params_.depositPeriod,
                msg.sender
            ),
            params_.amount,
            params_.isWrapped
        );

        // Update the asset liabilities for the caller (operator)
        _assetLiabilities[_getAssetLiabilitiesKey(params_.asset, msg.sender)] -= params_.amount;

        // Withdraw the funds from the vault to the recipient
        // This will revert if the asset is not configured
        (, actualAmount) = _withdrawAsset(params_.asset, params_.recipient, params_.amount);

        // Validate operator solvency after state updates
        _validateOperatorSolvency(params_.asset, msg.sender);

        return actualAmount;
    }

    /// @inheritdoc IDepositManager
    function getOperatorLiabilities(
        IERC20 asset_,
        address operator_
    ) external view returns (uint256) {
        return _assetLiabilities[_getAssetLiabilitiesKey(asset_, operator_)];
    }

    /// @notice Get the key for the asset liabilities mapping
    /// @dev    The key is the keccak256 of the asset address and the operator address
    function _getAssetLiabilitiesKey(
        IERC20 asset_,
        address operator_
    ) internal pure returns (bytes32) {
        return keccak256(abi.encode(address(asset_), operator_));
    }

    /// @notice Validates that an operator remains solvent after a withdrawal
    /// @dev    This function ensures that operator assets + borrowed amount >= operator liabilities
    ///         This is the core solvency constraint for the DepositManager
    ///
    ///         Notes:
    ///         - The solvency checks assume that the value of each vault share is increasing, and will not reduce.
    ///         - In a situation where the assets per share reduces below 1 (at the appropriate decimal scale), the solvency check will fail.
    ///
    /// @param asset_ The asset to validate solvency for
    /// @param operator_ The operator to validate solvency for
    function _validateOperatorSolvency(IERC20 asset_, address operator_) internal view {
        (, uint256 depositedSharesInAssets) = getOperatorAssets(asset_, operator_);
        bytes32 assetLiabilitiesKey = _getAssetLiabilitiesKey(asset_, operator_);
        uint256 operatorLiabilities = _assetLiabilities[assetLiabilitiesKey];
        uint256 borrowedAmount = _borrowedAmounts[assetLiabilitiesKey];

        if (operatorLiabilities > depositedSharesInAssets + borrowedAmount) {
            revert DepositManager_Insolvent(
                address(asset_),
                operatorLiabilities,
                depositedSharesInAssets,
                borrowedAmount
            );
        }
    }

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

    /// @inheritdoc IDepositManager
    /// @dev        Note that once set, an operator name cannot be changed.
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the admin or manager role
    ///             - The operator's name is already set
    ///             - The name is already in use by another operator
    ///             - The operator name is empty
    ///             - The operator name is not exactly 3 characters long
    ///             - The operator name contains characters that are not a-z or 0-9
    function setOperatorName(
        address operator_,
        string calldata name_
    ) external onlyEnabled onlyManagerOrAdminRole {
        // Validate that the name is not already set for the operator
        if (_operatorToName[operator_] != bytes3(0)) {
            revert DepositManager_OperatorNameSet(operator_);
        }

        // Validate that the name is not empty
        if (bytes(name_).length == 0) {
            revert DepositManager_OperatorNameInvalid();
        }

        // Validate that the name contains 3 characters
        if (bytes(name_).length != 3) {
            revert DepositManager_OperatorNameInvalid();
        }
        // Validate that the characters are a-z, 0-9
        {
            bytes memory nameBytes = bytes(name_);
            for (uint256 i = 0; i < 3; i++) {
                if (bytes1(nameBytes[i]) >= 0x61 && bytes1(nameBytes[i]) <= 0x7A) {
                    continue; // Lowercase letter
                }

                if (bytes1(nameBytes[i]) >= 0x30 && bytes1(nameBytes[i]) <= 0x39) {
                    continue; // Number
                }

                revert DepositManager_OperatorNameInvalid();
            }
        }

        /// forge-lint: disable-next-line(unsafe-typecast)
        bytes3 nameBytes3 = bytes3(bytes(name_));
        // Validate that the name isn't in use by another operator
        if (_operatorNames[nameBytes3]) revert DepositManager_OperatorNameInUse(name_);

        // Set the name
        _operatorToName[operator_] = nameBytes3;

        // Add to the operator names to prevent re-use
        _operatorNames[nameBytes3] = true;

        // Emit event
        emit OperatorNameSet(operator_, name_);
    }

    /// @inheritdoc IDepositManager
    function getOperatorName(address operator_) public view returns (string memory) {
        bytes memory nameBytes = new bytes(3);
        bytes3 operatorName = _operatorToName[operator_];
        if (operatorName == bytes3(0)) {
            return "";
        }

        nameBytes[0] = bytes1(operatorName[0]);
        nameBytes[1] = bytes1(operatorName[1]);
        nameBytes[2] = bytes1(operatorName[2]);

        // Convert bytes to string
        return string(nameBytes);
    }

    // ========== ASSET PERIOD ========== //

    /// @inheritdoc IDepositManager
    function isAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) public view override returns (AssetPeriodStatus memory status) {
        uint256 receiptTokenId = _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
            address(this),
            asset_,
            depositPeriod_,
            operator_
        );
        status.isConfigured = address(_assetPeriods[receiptTokenId].asset) != address(0);
        status.isEnabled = _assetPeriods[receiptTokenId].isEnabled;
        return status;
    }

    /// @inheritdoc IDepositManager
    /// @dev        This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the admin or manager role
    ///             - asset_ is the zero address
    ///             - minimumDeposit_ > depositCap_
    ///
    ///             Notes:
    ///             - A limitation of the current implementation is that the vault is assumed to be monotonically-increasing in value.
    ///             - The pairing of the asset and vault is immutable, to prevent a governance attack on user deposits.
    function addAsset(
        IERC20 asset_,
        IERC4626 vault_,
        uint256 depositCap_,
        uint256 minimumDeposit_
    ) external onlyEnabled onlyManagerOrAdminRole {
        _addAsset(asset_, vault_, depositCap_, minimumDeposit_);
    }

    /// @inheritdoc IDepositManager
    /// @dev        This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the admin or manager role
    ///             - asset_ is not configured
    ///             - The existing minimum deposit > depositCap_
    function setAssetDepositCap(
        IERC20 asset_,
        uint256 depositCap_
    ) external onlyEnabled onlyManagerOrAdminRole {
        _setAssetDepositCap(asset_, depositCap_);
    }

    /// @inheritdoc IDepositManager
    /// @dev        This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the admin or manager role
    ///             - asset_ is not configured
    ///             - minimumDeposit_ > the existing deposit cap
    function setAssetMinimumDeposit(
        IERC20 asset_,
        uint256 minimumDeposit_
    ) external onlyEnabled onlyManagerOrAdminRole {
        _setAssetMinimumDeposit(asset_, minimumDeposit_);
    }

    /// @inheritdoc IDepositManager
    /// @dev        This function is only callable by the manager or admin role
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the manager or admin role
    ///             - The asset has not been added via addAsset()
    ///             - The operator is the zero address
    ///             - The deposit period is 0
    ///             - The asset/deposit period/operator combination is already configured
    ///             - The operator name has not been set
    ///             - Receipt token creation fails (invalid parameters in ReceiptTokenManager)
    function addAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    )
        external
        onlyEnabled
        onlyManagerOrAdminRole
        onlyConfiguredAsset(asset_)
        returns (uint256 receiptTokenId)
    {
        // Validate that the operator is not the zero address
        if (operator_ == address(0)) revert DepositManager_ZeroAddress();

        // Validate that the deposit period is not 0
        if (depositPeriod_ == 0) revert DepositManager_OutOfBounds();

        // Validate that the asset and deposit period combination is not already configured
        if (isAssetPeriod(asset_, depositPeriod_, operator_).isConfigured) {
            revert DepositManager_AssetPeriodExists(address(asset_), depositPeriod_, operator_);
        }

        // Configure the ERC6909 receipt token and asset period atomically
        receiptTokenId = _setReceiptTokenData(asset_, depositPeriod_, operator_);

        // Emit event
        emit AssetPeriodConfigured(receiptTokenId, address(asset_), operator_, depositPeriod_);

        return receiptTokenId;
    }

    /// @inheritdoc IDepositManager
    /// @dev        This function is only callable by the manager or admin role
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the manager or admin role
    ///             - The asset/deposit period/operator combination does not exist
    ///             - The asset period is already enabled
    function enableAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    )
        external
        onlyEnabled
        onlyManagerOrAdminRole
        onlyAssetPeriodExists(asset_, depositPeriod_, operator_)
    {
        uint256 tokenId = _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
            address(this),
            asset_,
            depositPeriod_,
            operator_
        );
        if (_assetPeriods[tokenId].isEnabled) {
            revert DepositManager_AssetPeriodEnabled(address(asset_), depositPeriod_, operator_);
        }
        _assetPeriods[tokenId].isEnabled = true;

        // Emit event
        emit AssetPeriodEnabled(tokenId, address(asset_), operator_, depositPeriod_);
    }

    /// @inheritdoc IDepositManager
    /// @dev        This function is only callable by the manager or admin role
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the manager or admin role
    ///             - The asset/deposit period/operator combination does not exist
    ///             - The asset period is already disabled
    function disableAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    )
        external
        onlyEnabled
        onlyManagerOrAdminRole
        onlyAssetPeriodExists(asset_, depositPeriod_, operator_)
    {
        uint256 tokenId = _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
            address(this),
            asset_,
            depositPeriod_,
            operator_
        );
        if (!_assetPeriods[tokenId].isEnabled) {
            revert DepositManager_AssetPeriodDisabled(address(asset_), depositPeriod_, operator_);
        }
        _assetPeriods[tokenId].isEnabled = false;

        // Emit event
        emit AssetPeriodDisabled(tokenId, address(asset_), operator_, depositPeriod_);
    }

    /// @inheritdoc IDepositManager
    function getAssetPeriods() external view override returns (AssetPeriod[] memory assetPeriods) {
        // Get all token IDs owned by this contract
        uint256[] memory tokenIds = _ownedTokenIds.values();

        // Build the array of asset periods (all owned tokens should have valid asset periods)
        assetPeriods = new AssetPeriod[](tokenIds.length);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            assetPeriods[i] = _assetPeriods[tokenIds[i]];
        }

        return assetPeriods;
    }

    /// @inheritdoc IDepositManager
    function getAssetPeriod(uint256 tokenId_) public view override returns (AssetPeriod memory) {
        return _assetPeriods[tokenId_];
    }

    /// @inheritdoc IDepositManager
    function getAssetPeriod(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) public view override returns (AssetPeriod memory) {
        return _assetPeriods[getReceiptTokenId(asset_, depositPeriod_, operator_)];
    }

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

    /// @inheritdoc IDepositManager
    /// @dev        Notes:
    ///             - This function is only callable by addresses with the deposit operator role
    ///             - Given a low enough amount, the actual amount withdrawn may be 0. This function will not revert in such a case.
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the deposit operator role
    ///             - The recipient is the zero address
    ///             - The asset has not been added via addAsset()
    ///             - The amount exceeds the operator's available borrowing capacity
    ///             - The operator becomes insolvent after the withdrawal (assets + borrowed < liabilities)
    function borrowingWithdraw(
        BorrowingWithdrawParams calldata params_
    ) external onlyEnabled onlyRole(ROLE_DEPOSIT_OPERATOR) returns (uint256 actualAmount) {
        // Validate that the recipient is not the zero address
        if (params_.recipient == address(0)) revert DepositManager_ZeroAddress();

        // Validate that the asset is configured
        if (!_isConfiguredAsset(params_.asset)) revert AssetManager_NotConfigured();

        // Check borrowing capacity
        uint256 availableCapacity = getBorrowingCapacity(params_.asset, msg.sender);
        if (params_.amount > availableCapacity) {
            revert DepositManager_BorrowingLimitExceeded(
                address(params_.asset),
                msg.sender,
                params_.amount,
                availableCapacity
            );
        }

        // Withdraw the funds from the vault to the recipient
        // The value returned can also be zero
        (, actualAmount) = _withdrawAsset(params_.asset, params_.recipient, params_.amount);

        // Update borrowed amount
        // The requested amount is used, in order to avoid issues with insolvency checks
        _borrowedAmounts[_getAssetLiabilitiesKey(params_.asset, msg.sender)] += params_.amount;

        // Validate operator solvency after state updates
        _validateOperatorSolvency(params_.asset, msg.sender);

        // Emit event
        emit BorrowingWithdrawal(
            address(params_.asset),
            msg.sender,
            params_.recipient,
            actualAmount
        );

        return actualAmount;
    }

    /// @inheritdoc IDepositManager
    /// @dev        Notes:
    ///             - This function is only callable by addresses with the deposit operator role
    ///             - This function does not check for over-payment. It is expected to be handled by the calling contract.
    ///             - If the actual amount repaid is greater than the maximum amount provided, updates to the state variables are capped at the maximum amount.
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the deposit operator role
    ///             - The asset has not been added via addAsset()
    ///             - The payer has not approved DepositManager to spend the asset tokens
    ///             - The payer has insufficient asset token balance
    ///             - The asset is a fee-on-transfer token
    ///             - Zero shares would be deposited into the vault
    ///             - The operator becomes insolvent after the repayment (assets + borrowed < liabilities)
    function borrowingRepay(
        BorrowingRepayParams calldata params_
    ) external onlyEnabled onlyRole(ROLE_DEPOSIT_OPERATOR) returns (uint256 actualAmount) {
        // Validate that the asset is configured
        if (!_isConfiguredAsset(params_.asset)) revert AssetManager_NotConfigured();

        // Get the borrowing key
        bytes32 borrowingKey = _getAssetLiabilitiesKey(params_.asset, msg.sender);

        // Transfer funds from payer to this contract
        // This takes place before any state changes to avoid ERC777 re-entrancy
        // This purposefully does not check for over-payment, as it is expected to be handled by the calling contract
        (actualAmount, ) = _depositAsset(
            params_.asset,
            params_.payer,
            params_.amount,
            false // Do not enforce minimum deposit
        );

        // Update borrowed amount
        // Reduce by the actual amount, to avoid leakage
        // But cap at the max amount, to avoid an underflow for other loans
        _borrowedAmounts[borrowingKey] -= params_.maxAmount < actualAmount
            ? params_.maxAmount
            : actualAmount;

        // Validate operator solvency after borrowed amount change
        _validateOperatorSolvency(params_.asset, msg.sender);

        // Emit event
        emit BorrowingRepayment(address(params_.asset), msg.sender, params_.payer, actualAmount);

        return actualAmount;
    }

    /// @inheritdoc IDepositManager
    /// @dev        This function is only callable by addresses with the deposit operator role
    ///
    ///             This function reverts if:
    ///             - The contract is not enabled
    ///             - The caller does not have the deposit operator role
    ///             - The asset has not been added via addAsset()
    ///             - The amount exceeds the current borrowed amount for the operator
    ///             - The payer has insufficient receipt token balance
    ///             - The payer has not approved the caller to spend ERC6909 tokens
    ///             - The operator becomes insolvent after the default (assets + borrowed < liabilities)
    function borrowingDefault(
        BorrowingDefaultParams calldata params_
    ) external onlyEnabled onlyRole(ROLE_DEPOSIT_OPERATOR) {
        // Validate that the asset is configured
        if (!_isConfiguredAsset(params_.asset)) revert AssetManager_NotConfigured();

        // Get the borrowing key
        bytes32 borrowingKey = _getAssetLiabilitiesKey(params_.asset, msg.sender);

        // Check that the operator is not over-paying
        // This would cause accounting issues
        uint256 currentBorrowed = _borrowedAmounts[borrowingKey];
        if (currentBorrowed < params_.amount) {
            revert DepositManager_BorrowedAmountExceeded(
                address(params_.asset),
                msg.sender,
                params_.amount,
                currentBorrowed
            );
        }

        // Burn the receipt tokens from the payer
        _RECEIPT_TOKEN_MANAGER.burn(
            params_.payer,
            _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
                address(this),
                params_.asset,
                params_.depositPeriod,
                msg.sender
            ),
            params_.amount,
            false
        );

        // Update the asset liabilities for the caller (operator)
        _assetLiabilities[_getAssetLiabilitiesKey(params_.asset, msg.sender)] -= params_.amount;

        // Update the borrowed amount
        _borrowedAmounts[borrowingKey] -= params_.amount;

        // Validate operator solvency after borrowed amount change
        _validateOperatorSolvency(params_.asset, msg.sender);

        // No need to update the operator shares, as the balance has already been adjusted upon withdraw/repay

        // Emit event
        emit BorrowingDefault(address(params_.asset), msg.sender, params_.payer, params_.amount);
    }

    /// @inheritdoc IDepositManager
    function getBorrowedAmount(
        IERC20 asset_,
        address operator_
    ) public view returns (uint256 borrowed) {
        return _borrowedAmounts[_getAssetLiabilitiesKey(asset_, operator_)];
    }

    /// @inheritdoc IDepositManager
    function getBorrowingCapacity(
        IERC20 asset_,
        address operator_
    ) public view returns (uint256 capacity) {
        uint256 operatorLiabilities = _assetLiabilities[_getAssetLiabilitiesKey(asset_, operator_)];
        uint256 currentBorrowed = getBorrowedAmount(asset_, operator_);

        // This is unlikely to happen, but included to avoid a revert
        if (currentBorrowed >= operatorLiabilities) {
            return 0;
        }

        return operatorLiabilities - currentBorrowed;
    }

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

    function _setReceiptTokenData(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) internal returns (uint256 tokenId) {
        // Validate that the operator name is set
        string memory operatorName = getOperatorName(operator_);
        if (bytes(operatorName).length == 0) {
            revert DepositManager_OperatorNameNotSet(operator_);
        }

        // Create the receipt token via the factory
        tokenId = _RECEIPT_TOKEN_MANAGER.createToken(
            asset_,
            depositPeriod_,
            operator_,
            operatorName
        );

        // Record this token ID as owned by this contract
        _ownedTokenIds.add(tokenId);

        // Set the asset period data atomically
        _assetPeriods[tokenId] = AssetPeriod({
            isEnabled: true,
            depositPeriod: depositPeriod_,
            asset: address(asset_),
            operator: operator_
        });

        return tokenId;
    }

    /// @inheritdoc IDepositManager
    function getReceiptTokenId(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) public view override returns (uint256) {
        return
            _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
                address(this),
                asset_,
                depositPeriod_,
                operator_
            );
    }

    /// @inheritdoc IDepositManager
    function getReceiptTokenManager() external view override returns (IReceiptTokenManager) {
        return IReceiptTokenManager(address(_RECEIPT_TOKEN_MANAGER));
    }

    /// @inheritdoc IDepositManager
    function getReceiptTokenIds() external view override returns (uint256[] memory) {
        return _ownedTokenIds.values();
    }

    /// @inheritdoc IDepositManager
    function getReceiptToken(
        IERC20 asset_,
        uint8 depositPeriod_,
        address operator_
    ) external view override returns (uint256 tokenId, address wrappedToken) {
        tokenId = _RECEIPT_TOKEN_MANAGER.getReceiptTokenId(
            address(this),
            asset_,
            depositPeriod_,
            operator_
        );
        wrappedToken = _RECEIPT_TOKEN_MANAGER.getWrappedToken(tokenId);
        return (tokenId, wrappedToken);
    }

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

    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(BaseAssetManager, PolicyEnabler) returns (bool) {
        return
            interfaceId == type(IDepositManager).interfaceId ||
            BaseAssetManager.supportsInterface(interfaceId) ||
            PolicyEnabler.supportsInterface(interfaceId);
    }

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

    /// @notice Rescue any ERC20 token sent to this contract and send it to the TRSRY
    /// @dev    This function reverts if:
    ///         - The caller does not have the admin role
    ///         - token_ is a managed asset or vault
    ///         - token_ is the zero address
    ///
    /// @param  token_ The address of the ERC20 token to rescue
    function rescue(address token_) external onlyEnabled onlyAdminRole {
        // Validate that the token is not a managed asset or vault token
        uint256 configuredAssetsLength = _configuredAssets.length;
        for (uint256 i = 0; i < configuredAssetsLength; ) {
            IERC20 asset = _configuredAssets[i];
            // Prevent rescue of a configured asset or vault
            if (token_ == address(asset) || token_ == _assetConfigurations[asset].vault)
                revert DepositManager_CannotRescueAsset(token_);

            unchecked {
                i++;
            }
        }

        // Transfer the token balance to TRSRY
        // This will revert if the token is not a valid ERC20 or the zero address
        uint256 balance = ERC20(token_).balanceOf(address(this));
        address treasury = getModuleAddress(toKeycode("TRSRY"));
        if (balance > 0 && treasury != address(0)) {
            ERC20(token_).safeTransfer(treasury, balance);
            emit TokenRescued(token_, balance);
        }
    }
}
/// forge-lint: disable-end(asm-keccak256, mixed-case-function)
"
    },
    "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);
}
"
    },
    "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/policies/interfaces/deposits/IReceiptTokenManager.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IERC20} from "src/interfaces/IERC20.sol";
import {IERC6909} from "@openzeppelin-5.3.0/interfaces/draft-IERC6909.sol";
import {IERC6909Wrappable} from "src/interfaces/IERC6909Wrappable.sol";

/// @title IReceiptTokenManager
/// @notice Interface for the contract that creates and manages receipt tokens
interface IReceiptTokenManager is IERC6909, IERC6909Wrappable {
    // ========== EVENTS ========== //

    event TokenCreated(
        uint256 indexed tokenId,
        address indexed owner,
        address indexed asset,
        uint8 depositPeriod,
        address operator
    );

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

    error ReceiptTokenManager_TokenExists(uint256 tokenId);
    error ReceiptTokenManager_NotOwner(address caller, address owner);
    error ReceiptTokenManager_InvalidParams(string reason);

    // ========== FUNCTIONS ========== //

    /// @notice Creates a new receipt token
    /// @dev    The caller (msg.sender) becomes the owner of the token for security
    ///
    /// @param  asset_          The underlying asset
    /// @param  depositPeriod_  The deposit period
    /// @param  operator_       T

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Swap, Yield, Voting, Timelock, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xcb4e21eb404d80f3e1db781aad9ad6a1217fbbf2|verified:true|block:23747530|tx:0x979ea0c2c16869d6427c0e69798aec0d3ffd2db3fa52fda7954bc5cffc09b452|first_check:1762528448

Submitted on: 2025-11-07 16:14:09

Comments

Log in to comment.

No comments yet.